Compare commits

...
58 Commits
Author SHA1 Message Date
renovate[bot] f077d55a9f Update all dependencies 2024-06-17 19:14:22 +00: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
Norbert Dejlich 16c40319f4 [ci skip] Fix a publish workflow. 2023-10-14 21:44:55 +02:00
Michał WojtasandDMK a88c9e74fe GH-120 Add suport for event API. Add tagout - command to prevent combat log for a set duration (#120)
* Add causes for untag and tag of the player

* Add Cancellable implementation to Events.

* Extend uses of FightTagEvent in FightEffectController, add FightExtendTagEvent

* Small fixup (add this. to isCancelled variable)

* Add sketch for TagOut Commands, add listener for custom events, setup base for event api

* Code fix up. Add EventHandlers in EffectController

* Remove unnecessary file

* Add support for untagout command

* Follow DMK review suggestions

* Extract tag-out commands our of CombatCommand. Add one more user case scenario of removing tag-out

* codestyle fix

* Fix warn

* Follow DMK suggestions

* revert `assert` changes

* add space

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

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

* Follow another set of issues from DMK

* remove unused imports

* fix compilation error

* remove formatter where not needed

* Update src/main/java/com/eternalcode/combat/fight/tagout/TagOutCommand.java

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

* Add FightMessageController

* Small fixes

* Rename enum in CauseOfUnTag.java, fix block placement ban message

* Setup Api, setup Maven Repository - ready to publish

* Add module pointer before execute publish task.

* rename command from tag-out to tagout

* command aliases fix

* change "publish on push" to "publish on release"

* fix release types

---------

Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com>
2023-10-14 21:42:54 +02:00
cceeb82fef GH-116 Add Effects applied during Combat (#116)
* Add inCombatEffects based on custom Events

* Improve give back of potion effects.

* Fix merge conflicts and add okaeri bukkit serdes.

* Simplification of loops, new methods and optimization

* Change HashMap based on Player to UUID for better security

* Fix issues

* Update src/main/java/com/eternalcode/combat/config/implementation/PluginConfig.java

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

* Update src/main/java/com/eternalcode/combat/fight/effect/EffectService.java

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

* Update src/main/java/com/eternalcode/combat/fight/effect/EffectService.java

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

* Update src/main/java/com/eternalcode/combat/fight/effect/EffectService.java

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

* Update src/main/java/com/eternalcode/combat/fight/event/FightDeathEvent.java

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

* Update src/main/java/com/eternalcode/combat/fight/event/FightTagEvent.java

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

* Update src/main/java/com/eternalcode/combat/fight/event/FightDeathEvent.java

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

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

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

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

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

* Update src/main/java/com/eternalcode/combat/fight/effect/EffectService.java

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

* Codestyle fixes

* Codestyle fixes

* Remove unnesesary FightDeathEvent.java, refactor class names to match project style, fixes in codestyle, move to UUID event pass down instead of bukkit player.

* Add brackets

* Follow Rollczi's review

* Follow DMK's review.

* simple rename

---------

Co-authored-by: Jakubk15 <77227023+Jakubk15@users.noreply.github.com>
Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com>
2023-10-02 11:42:04 +02:00
DMKandRollczi 92535dd7a3 GH-86 Add a bossbar implementation (#113)
* Initial commit.

* Fix checkstyle.

* Follow rollczi review suggestions.

* Encapsulate FightBossBarService and FightBossBarRegistry.

* Added hiding boss bar after untagging player by command.

* Added the ability to customize the progress bar on the boss bar.

---------

Co-authored-by: Rollczi <ndejlich5@gmail.com>
2023-09-28 00:28:31 +02:00
DMK 3390534d98 GH-94 Fix item drop 2023-09-16 13:37:09 +02:00
DMK d45ce7406f GH-114 Fix eternal combat status message 2023-09-16 13:35:29 +02:00
Martin SulikowskiandDominik 5861f70010 GH-109 Migrate configuration to use the okaeri configuration (#111)
* Initial commit.

* Follow rollczi suggestions.

* Fixed a bug with file configuration creation.

* Jar file size reduced.

* Added relocating javassist and org.apache.commons.

---------

Co-authored-by: Dominik <dominiks8318@gmail.com>
2023-09-15 18:28:07 +02:00
Martin Sulikowski 7940a2663b Release v1.1.1 2023-08-25 00:20:21 +02:00
106 changed files with 2587 additions and 1395 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@66535aaf56f831b35e3a8481c9c99b665b84dd45
with:
arguments: shadowJar
- name: Upload a Build Artifact
uses: actions/upload-artifact@v3.1.2
uses: actions/upload-artifact@v4.3.3
with:
name: 'Successfully build EternalCombat'
path: build/libs/*.jar
path: eternalcombat-plugin/build/libs/*.jar
+28
View File
@@ -0,0 +1,28 @@
name: Publish EternalCombat API
on:
release:
branches: [ master ]
types: [ published ]
jobs:
publish:
runs-on: ubuntu-latest
environment: deployment
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
cache: 'gradle'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Publish with Gradle
run: ./gradlew clean build eternalcombat-api:publish
env:
ETERNAL_CODE_MAVEN_USERNAME: ${{ secrets.ETERNAL_CODE_MAVEN_USERNAME }}
ETERNAL_CODE_MAVEN_PASSWORD: ${{ secrets.ETERNAL_CODE_MAVEN_PASSWORD }}
+38 -1
View File
@@ -15,7 +15,7 @@
### 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.20.6, 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.
@@ -35,6 +35,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.2.2")
```
with Maven:
```xml
<dependency>
<groupId>com.eternalcode</groupId>
<artifactId>eternalcombat-api</artifactId>
<version>1.2.2</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

-129
View File
@@ -1,129 +0,0 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
plugins {
`java-library`
checkstyle
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"
}
group = "com.eternalcode"
version = "1.1.0"
checkstyle {
toolVersion = "10.12.2"
configFile = file("${rootDir}/config/checkstyle/checkstyle.xml")
maxErrors = 0
maxWarnings = 0
}
repositories {
mavenCentral()
mavenLocal()
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://repo.panda-lang.org/releases") }
maven { url = uri("https://maven.enginehub.org/repo/") }
}
dependencies {
// Spigot api
compileOnly("org.spigotmc:spigot-api:1.19.3-R0.1-SNAPSHOT")
// kyori
implementation("net.kyori:adventure-platform-bukkit:4.3.0")
implementation("net.kyori:adventure-text-minimessage:4.14.0")
// litecommands
implementation("dev.rollczi.litecommands:bukkit-adventure:2.8.9")
// cdn configs
implementation("net.dzikoysk:cdn:1.14.4") {
exclude(group = "org.jetbrains.kotlin")
}
// GitCheck
implementation("com.eternalcode:gitcheck:1.0.0")
// commons
implementation("commons-io:commons-io:2.13.0")
// bstats
implementation("org.bstats:bstats-bukkit:3.0.2")
// worldguard
compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.0.9")
// 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")
}
bukkit {
main = "com.eternalcode.combat.CombatPlugin"
author = "EternalCodeTeam"
apiVersion = "1.13"
prefix = "EternalCombat"
name = "EternalCombat"
softDepend = listOf("WorldGuard")
version = "${project.version}"
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
tasks.getByName<Test>("test") {
useJUnitPlatform()
}
tasks.withType<JavaCompile> {
options.compilerArgs = listOf("-Xlint:deprecation", "-parameters")
options.encoding = "UTF-8"
}
tasks {
runServer {
minecraftVersion("1.19.4")
}
}
tasks.withType<ShadowJar> {
archiveFileName.set("EternalCombat v${project.version}.jar")
dependsOn("checkstyleMain")
dependsOn("checkstyleTest")
dependsOn("test")
exclude(
"org/intellij/lang/annotations/**",
"org/jetbrains/annotations/**",
"META-INF/**",
"javax/**"
)
val prefix = "com.eternalcode.combat.libs"
listOf(
"panda.std",
"panda.utilities",
"org.panda-lang",
"net.dzikoysk",
"net.kyori",
"org.bstats",
"dev.rollczi.litecommands",
"com.eternalcode.gitcheck",
"org.json.simple",
"commons-io"
).forEach { pack ->
relocate(pack, "$prefix.$pack")
}
}
+29
View File
@@ -0,0 +1,29 @@
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
gradlePluginPortal()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.24")
implementation("com.github.johnrengelman:shadow:8.1.1")
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>())
}
}
+34
View File
@@ -0,0 +1,34 @@
object Versions {
const val SPIGOT_API = "1.17.1-R0.1-SNAPSHOT"
const val JUNIT_JUPITER_API = "5.10.2"
const val JUNIT_JUPITER_PARAMS = "5.10.2"
const val JUNIT_JUPITER_ENGINE = "5.10.2"
const val ETERNALCODE_COMMONS = "1.1.3"
// TODO: Multification.
const val ADVENTURE_PLATFORM_BUKKIT = "4.3.3"
const val ADVENTURE_TEXT_MINIMESSAGE = "4.17.0"
const val LITE_COMMANDS = "2.8.9"
const val OKAERI_CONFIGS_YAML_BUKKIT = "5.0.2"
const val OKAERI_CONFIGS_SERDES_COMMONS = "5.0.2"
const val OKAERI_CONFIGS_SERDES_BUKKIT = "5.0.2"
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.10"
}
@@ -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,36 @@
plugins {
`java-library`
checkstyle
}
group = "com.eternalcode"
version = "1.2.2"
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.encoding = "UTF-8"
}
@@ -0,0 +1,34 @@
plugins {
`java-library`
`maven-publish`
}
group = "com.eternalcode"
version = "1.2.2"
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,14 @@
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/") }
}
+45
View File
@@ -0,0 +1,45 @@
plugins {
`eternalcombat-java`
`eternalcombat-repositories`
`eternalcombat-publish`
`eternalcombat-java-unit-test`
}
dependencies {
// Spigot api
compileOnlyApi("org.spigotmc:spigot-api:${Versions.SPIGOT_API}")
// kyori
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:${Versions.LITE_COMMANDS}")
// Okaeri configs
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:${Versions.PANDA_UTILITIES}")
// GitCheck
api("com.eternalcode:gitcheck:${Versions.GIT_CHECK}")
// commons
api("commons-io:commons-io:${Versions.APACHE_COMMONS}")
// bstats
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:${Versions.WORLD_GUARD_BUKKIT}")
}
@@ -1,8 +1,12 @@
package com.eternalcode.combat;
import com.eternalcode.combat.config.ConfigManager;
import com.eternalcode.combat.config.ConfigService;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.event.CauseOfTag;
import com.eternalcode.combat.fight.event.CauseOfUnTag;
import com.eternalcode.combat.fight.event.FightTagEvent;
import com.eternalcode.combat.fight.event.FightUntagEvent;
import com.eternalcode.combat.notification.NotificationAnnouncer;
import dev.rollczi.litecommands.argument.Arg;
import dev.rollczi.litecommands.command.async.Async;
@@ -20,101 +24,114 @@ import java.util.UUID;
public class CombatCommand {
private final FightManager fightManager;
private final ConfigManager configManager;
private final ConfigService configService;
private final NotificationAnnouncer announcer;
private final PluginConfig config;
public CombatCommand(FightManager fightManager, ConfigManager configManager, NotificationAnnouncer announcer, PluginConfig config) {
public CombatCommand(FightManager fightManager, ConfigService configService, NotificationAnnouncer announcer, PluginConfig config) {
this.fightManager = fightManager;
this.configManager = configManager;
this.configService = configService;
this.announcer = announcer;
this.config = config;
}
@Execute(route = "status", required = 1)
@Permission("eternalcombat.status")
void status(CommandSender player, @Arg Player target) {
void status(CommandSender sender, @Arg Player target) {
UUID targetUniqueId = target.getUniqueId();
PluginConfig.Messages messages = this.config.messages;
Formatter formatter = new Formatter()
.register("{PLAYER}", target.getName());
this.announcer.sendMessage(player, this.fightManager.isInCombat(targetUniqueId)
this.announcer.sendMessage(sender, this.fightManager.isInCombat(targetUniqueId)
? formatter.format(messages.admin.playerInCombat)
: formatter.format(messages.admin.adminPlayerNotInCombat));
: formatter.format(messages.admin.playerNotInCombat));
}
@Execute(route = "tag", required = 1)
@Permission("eternalcombat.tag")
void tag(Player player, @Arg Player target) {
UUID targetUniqueId = target.getUniqueId();
void tag(CommandSender sender, @Arg Player target) {
UUID targetUniqueId = target.getUniqueId();
Duration time = this.config.settings.combatDuration;
Formatter formatter = new Formatter()
.register("{PLAYER}", target.getName());
this.fightManager.tag(targetUniqueId, time);
FightTagEvent event = this.fightManager.tag(targetUniqueId, time, CauseOfTag.COMMAND);
if (event.isCancelled()) {
this.announcer.sendMessage(sender, event.getCancelMessage());
return;
}
String format = formatter.format(this.config.messages.admin.adminTagPlayer);
this.announcer.sendMessage(player, format);
this.announcer.sendMessage(sender, format);
}
@Execute(route = "tag", required = 2)
@Permission("eternalcombat.tag")
public void tagMultiple(Player player, @Arg Player firstTarget, @Arg Player secondTarget) {
void tagMultiple(CommandSender sender, @Arg Player firstTarget, @Arg Player secondTarget) {
Duration combatTime = this.config.settings.combatDuration;
PluginConfig.Messages messages = this.config.messages;
UUID firstTargetUniqueId = firstTarget.getUniqueId();
UUID secondTargetUniqueId = secondTarget.getUniqueId();
UUID playerUniqueId = player.getUniqueId();
if (playerUniqueId.equals(firstTargetUniqueId) || playerUniqueId.equals(secondTargetUniqueId)) {
this.announcer.sendMessage(player, messages.admin.adminCannotTagSelf);
if (sender.equals(firstTarget) || sender.equals(secondTarget)) {
this.announcer.sendMessage(sender, messages.admin.adminCannotTagSelf);
return;
}
this.fightManager.tag(firstTargetUniqueId, combatTime);
this.fightManager.tag(secondTargetUniqueId, combatTime);
FightTagEvent firstTagEvent = this.fightManager.tag(firstTarget.getUniqueId(), combatTime, CauseOfTag.COMMAND);
FightTagEvent secondTagEvent = this.fightManager.tag(secondTarget.getUniqueId(), combatTime, CauseOfTag.COMMAND);
Formatter formatter = new Formatter()
.register("{FIRST_PLAYER}", firstTarget.getName())
.register("{SECOND_PLAYER}", secondTarget.getName());
String format = formatter.format(messages.admin.adminTagMultiplePlayers);
this.announcer.sendMessage(player, format);
this.announcer.sendMessage(firstTarget, messages.playerTagged);
this.announcer.sendMessage(secondTarget, messages.playerTagged);
}
if (firstTagEvent.isCancelled()) {
this.announcer.sendMessage(sender, firstTagEvent.getCancelMessage());
}
@Execute(route = "untag", required = 1)
@Permission("eternalcombat.untag")
void untag(Player player, @Arg Player target) {
UUID targetUniqueId = target.getUniqueId();
if (secondTagEvent.isCancelled()) {
this.announcer.sendMessage(sender, firstTagEvent.getCancelMessage());
}
if (!this.fightManager.isInCombat(targetUniqueId)) {
this.announcer.sendMessage(player, this.config.messages.admin.adminPlayerNotInCombat);
if (firstTagEvent.isCancelled() && secondTagEvent.isCancelled()) {
return;
}
this.announcer.sendMessage(target, this.config.messages.playerUntagged);
this.fightManager.untag(targetUniqueId);
Formatter formatter = new Formatter()
.register("{PLAYER}", target.getName());
String format = formatter.format(this.config.messages.admin.adminUntagPlayer);
this.announcer.sendMessage(player, format);
this.announcer.sendMessage(sender, format);
}
@Async
@Execute(route = "reload")
@Permission("eternalcombat.reload")
void execute(CommandSender player) {
this.configManager.reload();
this.configService.reload();
this.announcer.sendMessage(player, this.config.messages.admin.reload);
}
@Execute(route = "untag", required = 1)
@Permission("eternalcombat.untag")
void untag(Player sender, @Arg Player target) {
UUID targetUniqueId = target.getUniqueId();
if (!this.fightManager.isInCombat(targetUniqueId)) {
this.announcer.sendMessage(sender, this.config.messages.admin.adminPlayerNotInCombat);
return;
}
FightUntagEvent event = this.fightManager.untag(targetUniqueId, CauseOfUnTag.COMMAND);
if (event.isCancelled()) {
return;
}
Formatter formatter = new Formatter()
.register("{PLAYER}", target.getName());
String format = formatter.format(this.config.messages.admin.adminUntagPlayer);
this.announcer.sendMessage(sender, format);
}
}
@@ -0,0 +1,30 @@
package com.eternalcode.combat;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.drop.DropKeepInventoryManager;
import com.eternalcode.combat.drop.DropManager;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.effect.FightEffectService;
import com.eternalcode.combat.fight.pearl.FightPearlManager;
import com.eternalcode.combat.fight.tagout.FightTagOutService;
import com.eternalcode.combat.region.RegionProvider;
public interface EternalCombatApi {
FightManager getFightManager();
RegionProvider getRegionProvider();
FightPearlManager getFightPearlManager();
FightTagOutService getFightTagOutService();
FightEffectService getFightEffectService();
DropManager getDropManager();
DropKeepInventoryManager getDropKeepInventoryManager();
PluginConfig getPluginConfig();
}
@@ -0,0 +1,31 @@
package com.eternalcode.combat;
public final class EternalCombatProvider {
private static EternalCombatApi ETERNAL_COMBAT_API;
public static EternalCombatApi provide() {
if (ETERNAL_COMBAT_API == null) {
throw new IllegalStateException("EternalCombatApi has not been initialized yet!");
}
return ETERNAL_COMBAT_API;
}
static void initialize(EternalCombatApi eternalCombatApi) {
if (ETERNAL_COMBAT_API != null) {
throw new IllegalStateException("EternalCombatApi has already been initialized!");
}
ETERNAL_COMBAT_API = eternalCombatApi;
}
static void deinitialize() {
if (ETERNAL_COMBAT_API == null) {
throw new IllegalStateException("EternalCombatApi has not been initialized yet!");
}
ETERNAL_COMBAT_API = null;
}
}
@@ -0,0 +1,41 @@
package com.eternalcode.combat.config;
import com.eternalcode.combat.notification.serializer.NotificationSerializer;
import eu.okaeri.configs.ConfigManager;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.serdes.commons.SerdesCommons;
import eu.okaeri.configs.yaml.bukkit.YamlBukkitConfigurer;
import eu.okaeri.configs.yaml.bukkit.serdes.SerdesBukkit;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
public class ConfigService {
private final Set<OkaeriConfig> configs = new HashSet<>();
public <T extends OkaeriConfig> T create(Class<T> config, File file) {
T configFile = ConfigManager.create(config);
configFile.withConfigurer(new YamlBukkitConfigurer(), new SerdesCommons(), new SerdesBukkit());
configFile.withSerdesPack(registry -> {
registry.register(new NotificationSerializer());
});
configFile.withBindFile(file);
configFile.withRemoveOrphans(true);
configFile.saveDefaults();
configFile.load(true);
this.configs.add(configFile);
return configFile;
}
public void reload() {
for (OkaeriConfig config : this.configs) {
config.load();
}
}
}
@@ -0,0 +1,230 @@
package com.eternalcode.combat.config.implementation;
import com.eternalcode.combat.WhitelistBlacklistMode;
import com.eternalcode.combat.drop.DropSettings;
import com.eternalcode.combat.fight.effect.FightEffectSettings;
import com.eternalcode.combat.fight.pearl.FightPearlSettings;
import com.eternalcode.combat.notification.Notification;
import com.eternalcode.combat.notification.implementation.ActionBarNotification;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment;
import org.bukkit.Material;
import org.bukkit.entity.EntityType;
import org.bukkit.event.entity.EntityDamageEvent;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
public class PluginConfig extends OkaeriConfig {
@Comment("# Do you want to change the plugin settings?")
public Settings settings = new Settings();
@Comment({" ", "# Ender pearl settings"})
public FightPearlSettings pearl = new FightPearlSettings();
@Comment({" ", "# Custom effects settings"})
public FightEffectSettings effect = new FightEffectSettings();
@Comment({" ", "# Set a custom way for a player's items to drop on death (if in combat)"})
public DropSettings dropSettings = new DropSettings();
public static class Settings extends OkaeriConfig {
@Comment("# Whether the player should receive information about new plugin updates upon joining the server")
public boolean shouldReceivePluginUpdates = true;
@Comment("# The duration of combat in seconds")
public Duration combatDuration = Duration.ofSeconds(20);
@Comment("# List of worlds to ignore")
public List<String> worldsToIgnore = List.of(
"your_world"
);
@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 radius of the blocked region if you aren't using WorldGuard basen on default spawn region!")
public int blockedRegionRadius = 10;
@Comment("# Release attacker after victim dies?")
public boolean shouldReleaseAttacker = true;
@Comment("# If you want to exclude admins from combat, set this to true")
public boolean excludeAdminFromCombat = false;
@Comment("# Command blocking mode, available modes: WHITELIST, BLACKLIST")
public WhitelistBlacklistMode commandBlockingMode = WhitelistBlacklistMode.BLACKLIST;
@Comment({
"# List of commands based on the mode above",
"# Based on BLACKLIST mode, all commands in the list are blocked, and all others are allowed",
"# Based on WHITELIST mode, all commands in the list are allowed, and all others are blocked",
})
public List<String> blockedCommands = List.of(
"gamemode",
"spawn",
"tp"
);
@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;
@Comment("# Block the opening of inventory?")
public boolean shouldPreventInventoryOpening = true;
@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({
"# Disable placing specific blocks?",
"# If you want to block all blocks, enable shouldPreventBlockPlacing and make this list empty",
"# If you want to disable placing only specific blocks, enable shouldPreventBlockPlacing and add blocks to this list above",
"# If you want to disable this feature completely, disable shouldPreventBlockPlacing option above",
})
public List<Material> specificBlocksToPreventPlacing = List.of();
@Comment("# Do You want to enable combat log for non-player causes of damage? - Set to false to disable")
public boolean shouldEnableDamageCauses = false;
@Comment("# Select the mode for damage causes, available modes: WHITELIST, BLACKLIST")
public WhitelistBlacklistMode damageCausesMode = WhitelistBlacklistMode.WHITELIST;
@Comment({
"# After selecting the mode above, select the causes of damage to be logged",
"# You can find a list of all causes here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityDamageEvent.DamageCause.html",
"# While using Whitelist mode the player will get a combat log after the damage from the list below",
"# While using Blacklist mode the player will get a combat log after any damage non-listed below",
})
public List<EntityDamageEvent.DamageCause> damageCausesToLog = List.of(
EntityDamageEvent.DamageCause.LAVA,
EntityDamageEvent.DamageCause.CONTACT,
EntityDamageEvent.DamageCause.FIRE,
EntityDamageEvent.DamageCause.FIRE_TICK
);
@Comment({
"# After what type of projectile entity should not tag the player as fighter?",
"# You can find a list of all entity types here: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html"
})
public List<EntityType> disabledProjectileEntities = List.of(
EntityType.ENDER_PEARL
);
}
@Comment({" ", "# Do you want to change the plugin messages?"})
public Messages messages = new Messages();
public static class Messages extends OkaeriConfig {
@Comment("# Do you want to change the admin messages?")
public AdminMessages admin = new AdminMessages();
@Comment({
" ",
"# Combat log notification",
"# You can use {TIME} variable to display the time left in combat",
"# Notification types: CHAT, ACTION_BAR, TITLE, SUB_TITLE, BOSS_BAR",
" ",
"# BossBar progress: This is the value of the progress bar. Set it to -1.0 to show the remaining combat time",
"# BossBar colors: https://javadoc.io/static/net.kyori/adventure-api/4.14.0/net/kyori/adventure/bossbar/BossBar.Color.html",
"# BossBar overlays: https://javadoc.io/static/net.kyori/adventure-api/4.14.0/net/kyori/adventure/bossbar/BossBar.Overlay.html"
})
public Notification combatNotification = new ActionBarNotification("&dCombat ends in: &f{TIME}");
@Comment("# Message sent when the player does not have permission to perform a command")
public String noPermission = "&cYou don't have permission to perform this command!";
@Comment("# Message sent when the specified player could not be found")
public String playerNotFound = "&cThe specified player could not be found!";
@Comment("# Message sent when the player enters combat")
public String playerTagged = "&cYou are in combat, do not leave the server!";
@Comment("# Message sent when the player leaves combat")
public String playerUntagged = "&aYou are no longer in combat! You can safely leave the server.";
@Comment("# This is broadcast when the player is in combat and logs out")
public String playerLoggedOutDuringCombat = "&c{PLAYER} logged off during the fight!";
@Comment({
"# Message sent when the player is in combat and tries to use a disabled command",
"# you can configure the list of disabled commands in the blockedCommands section of the config.yml file"
})
public String commandDisabledDuringCombat = "&cUsing this command during combat is prohibited!";
@Comment("# Message sent when player tries to use a command with invalid arguments")
public String invalidCommandUsage = "&7Correct usage: &e{USAGE}";
@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 enter a region")
public String cantEnterOnRegion = "&cYou can't enter on this region during combat!";
public static class AdminMessages extends OkaeriConfig {
@Comment("# Message sent when the configuration is reloaded")
public String reload = "&aConfiguration has been successfully reloaded!";
@Comment("# Message sent when console tries to use a command that is only for players")
public String onlyForPlayers = "&cThis command is only available to players!";
@Comment("# Message sent to admin when they tag a player")
public String adminTagPlayer = "&7You have tagged &e{PLAYER}";
@Comment("# Message sent when a player is tagged by an admin")
public String adminTagMultiplePlayers = "&7You have tagged &e{FIRST_PLAYER}&7 and &e{SECOND_PLAYER}&7.";
@Comment("# Message sent to admin when they remove a player from combat")
public String adminUntagPlayer = "&7You have removed &e{PLAYER}&7 from the fight.";
@Comment("# Message sent when the player is not in combat")
public String adminPlayerNotInCombat = "&cThis player is not in combat!";
@Comment("# Message sent when the player is in combat")
public String playerInCombat = "&c{PLAYER} is currently in combat!";
@Comment("# Message sent when a player is not in combat")
public String playerNotInCombat = "&a{PLAYER} is not currently in combat.";
@Comment("# Message sent when an admin tries to tag themselves")
public String adminCannotTagSelf = "&cYou cannot tag yourself!";
@Comment("# Message sent when an admin disables the ability to get tagged for some time")
public String adminTagOutSelf = "&7Successfully disabled tag for Yourself! You will be taggable after &e{TIME} ";
@Comment("# Message sent when an admin disables the ability to get tagged for some time for other player")
public String adminTagOut = "&7Successfully disabled tag for &e{PLAYER}! They will be taggable after &e{TIME} ";
@Comment("# Message sent to the player whom the ability to get tagged for some time has been disabled")
public String playerTagOut = "&7You will be taggable in &e{TIME} !";
@Comment("# Message sent when an admin reenables the ability to get tagged for the player")
public String adminTagOutOff = "&7Successfully enabled tag for &e{PLAYER}!";
@Comment("# Message sent to the player whom the ability to get tagged has been reenabled")
public String playerTagOutOff = "&7You are now taggable!";
@Comment("# Message sent when player cannot be tagged because they have enabled tag-out")
public String adminTagOutCanceled = "&cCannot tag this player due to tag-out!";
}
}
}
@@ -1,41 +1,31 @@
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;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Drop {
private final Player player;
private final Player killer;
private final FightTag fightTag;
private final FightDeathCause deathCause;
private List<ItemStack> droppedItems;
private int droppedExp;
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;
}
public List<ItemStack> getDroppedItems() {
return this.droppedItems;
}
public void setDroppedItems(List<ItemStack> droppedItems) {
this.droppedItems = droppedItems;
return Collections.unmodifiableList(this.droppedItems);
}
public Player getPlayer() {
@@ -46,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;
}
@@ -62,10 +44,6 @@ public class Drop {
return this.droppedExp;
}
public void setDroppedExp(int droppedExp) {
this.droppedExp = droppedExp;
}
public static Builder builder() {
return new Builder();
}
@@ -74,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;
@@ -89,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;
@@ -113,8 +79,6 @@ public class Drop {
return new Drop(
this.player,
this.killer,
this.fightTag,
this.deathCause,
this.droppedItems,
this.droppedExp
);
@@ -0,0 +1,79 @@
package com.eternalcode.combat.drop;
import com.eternalcode.combat.fight.FightManager;
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;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import java.util.List;
import java.util.UUID;
public class DropController implements Listener {
private final DropManager dropManager;
private final DropKeepInventoryManager keepInventoryManager;
private final DropSettings dropSettings;
private final FightManager fightManager;
public DropController(DropManager dropManager, DropKeepInventoryManager keepInventoryManager, DropSettings dropSettings, FightManager fightManager) {
this.dropManager = dropManager;
this.keepInventoryManager = keepInventoryManager;
this.dropSettings = dropSettings;
this.fightManager = fightManager;
}
@EventHandler(priority = EventPriority.LOW)
void onPlayerDeath(PlayerDeathEvent event) {
Player player = event.getEntity();
DropType dropType = this.dropSettings.dropType;
if (dropType == DropType.UNCHANGED || !this.fightManager.isInCombat(player.getUniqueId())) {
return;
}
List<ItemStack> drops = event.getDrops();
Drop drop = Drop.builder()
.player(player)
.killer(player.getKiller())
.droppedItems(drops)
.droppedExp(player.getTotalExperience())
.build();
DropResult result = this.dropManager.modify(dropType, drop);
if (result == null) {
return;
}
drops.clear();
drops.addAll(result.droppedItems());
this.keepInventoryManager.addItems(player.getUniqueId(), result.removedItems());
if (this.dropSettings.affectExperience) {
event.setDroppedExp(drop.getDroppedExp());
}
}
@EventHandler
void onPlayerRespawn(PlayerRespawnEvent event) {
Player player = event.getPlayer();
UUID playerUniqueId = player.getUniqueId();
if (this.keepInventoryManager.hasItems(playerUniqueId)) {
PlayerInventory playerInventory = player.getInventory();
ItemStack[] itemsToGive = this.keepInventoryManager.nextItems(playerUniqueId)
.toArray(new ItemStack[0]);
playerInventory.addItem(itemsToGive);
}
}
}
@@ -0,0 +1,38 @@
package com.eternalcode.combat.drop;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class DropKeepInventoryManager {
private final Map<UUID, List<ItemStack>> itemsToGiveAfterRespawn = new HashMap<>();
public void addItem(UUID uuid, ItemStack item) {
this.itemsToGiveAfterRespawn.computeIfAbsent(uuid, k -> new ArrayList<>()).add(item);
}
public void addItems(UUID uuid, List<ItemStack> item) {
item.forEach(i -> this.addItem(uuid, i));
}
public boolean hasItems(UUID uuid) {
return this.itemsToGiveAfterRespawn.containsKey(uuid);
}
public List<ItemStack> nextItems(UUID uuid) {
List<ItemStack> itemStacks = this.itemsToGiveAfterRespawn.remove(uuid);
if (itemStacks == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(itemStacks);
}
}
@@ -25,10 +25,12 @@ public class DropManager {
this.modifiers.put(dropType, dropModifier);
}
public void modify(DropType dropType, Drop drop) {
public DropResult modify(DropType dropType, Drop drop) {
if (!this.modifiers.containsKey(dropType)) {
throw new RuntimeException("No drop modifier found for type '%s'".formatted(dropType.name()));
}
this.modifiers.get(dropType).modifyDrop(drop);
return this.modifiers.get(dropType).modifyDrop(drop);
}
}
@@ -4,6 +4,6 @@ public interface DropModifier {
DropType getDropType();
void modifyDrop(Drop drop);
DropResult modifyDrop(Drop drop);
}
@@ -0,0 +1,24 @@
package com.eternalcode.combat.drop;
import org.bukkit.inventory.ItemStack;
import java.util.Collections;
import java.util.List;
public record DropResult(
List<ItemStack> droppedItems,
List<ItemStack> removedItems,
int droppedExp
) {
@Override
public List<ItemStack> droppedItems() {
return Collections.unmodifiableList(this.droppedItems);
}
@Override
public List<ItemStack> removedItems() {
return Collections.unmodifiableList(this.removedItems);
}
}
@@ -0,0 +1,23 @@
package com.eternalcode.combat.drop;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment;
public class DropSettings extends OkaeriConfig {
@Comment({
"# UNCHANGED - The default way of item drop defined by the engine",
"# PERCENT - Drops a fixed percentage of items",
"# PLAYERS_HEALTH - Drops inverted percentage of the player's health (i.e. if the player has, for example, 80% HP, he will drop 20% of items. Only works when the player escapes from combat by quiting game)"
})
public DropType dropType = DropType.UNCHANGED;
@Comment("# What percentage of items should drop from the player? (Only if Drop Type is set to PERCENT)")
public int dropItemPercent = 100;
@Comment("# This option is responsible for the lowest percentage of the player that can drop (i.e. if the player leaves the game while he has 100% of his HP, the percentage of items that is set in this option will drop, if you set this option to 0, then nothing will drop from such a player)")
public int playersHealthPercentClamp = 20;
@Comment("# Does the drop modification affect the experience drop?")
public boolean affectExperience = false;
}
@@ -1,11 +1,13 @@
package com.eternalcode.combat.drop.impl;
import com.eternalcode.combat.drop.DropModifier;
import com.eternalcode.combat.drop.Drop;
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.util.InventoryUtil;
import com.eternalcode.combat.util.MathUtil;
import com.eternalcode.combat.util.RemoveItemResult;
import org.bukkit.inventory.ItemStack;
import java.util.List;
@@ -24,17 +26,15 @@ public class PercentDropModifier implements DropModifier {
}
@Override
public void modifyDrop(Drop drop) {
public DropResult modifyDrop(Drop drop) {
int dropItemPercent = 100 - MathUtil.clamp(this.settings.dropItemPercent, 0, 100);
List<ItemStack> droppedItems = drop.getDroppedItems();
int itemsToDelete = InventoryUtil.calculateItemsToDelete(dropItemPercent, droppedItems, ItemStack::getAmount);
int droppedExp = MathUtil.getRoundedCountFromPercentage(dropItemPercent, drop.getDroppedExp());
InventoryUtil.removeRandomItems(droppedItems, itemsToDelete);
RemoveItemResult result = InventoryUtil.removeRandomItems(droppedItems, itemsToDelete);
drop.setDroppedItems(droppedItems);
drop.setDroppedExp(droppedExp);
return new DropResult(result.restItems(), result.removedItems(), droppedExp);
}
}
@@ -2,24 +2,29 @@ package com.eternalcode.combat.drop.impl;
import com.eternalcode.combat.drop.Drop;
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;
import org.bukkit.attribute.Attribute;
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
@@ -28,18 +33,21 @@ public class PlayersHealthDropModifier implements DropModifier {
}
@Override
public void modifyDrop(Drop drop) {
if (drop.getDeathCause() != FightDeathCause.ESCAPE) {
return;
public DropResult modifyDrop(Drop drop) {
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);
@@ -47,9 +55,8 @@ public class PlayersHealthDropModifier implements DropModifier {
int itemsToDelete = InventoryUtil.calculateItemsToDelete(reversedPercent, droppedItems, ItemStack::getAmount);
int droppedExp = MathUtil.getRoundedCountFromPercentage(reversedPercent, drop.getDroppedExp());
InventoryUtil.removeRandomItems(droppedItems, itemsToDelete);
RemoveItemResult result = InventoryUtil.removeRandomItems(droppedItems, itemsToDelete);
drop.setDroppedItems(droppedItems);
drop.setDroppedExp(droppedExp);
return new DropResult(result.restItems(), result.removedItems(), droppedExp);
}
}
@@ -0,0 +1,20 @@
package com.eternalcode.combat.event;
import org.bukkit.Server;
import org.bukkit.event.Event;
public class EventCaller {
private final Server server;
public EventCaller(Server server) {
this.server = server;
}
public <T extends Event> T publishEvent(T event) {
this.server.getPluginManager().callEvent(event);
return event;
}
}
@@ -1,5 +1,11 @@
package com.eternalcode.combat.fight;
import com.eternalcode.combat.fight.event.CauseOfTag;
import com.eternalcode.combat.fight.event.CauseOfUnTag;
import com.eternalcode.combat.fight.event.FightTagEvent;
import com.eternalcode.combat.fight.event.FightUntagEvent;
import com.eternalcode.combat.event.EventCaller;
import java.time.Duration;
import java.time.Instant;
import java.util.Collection;
@@ -11,6 +17,11 @@ import java.util.concurrent.ConcurrentHashMap;
public class FightManager {
private final Map<UUID, FightTag> fights = new ConcurrentHashMap<>();
private final EventCaller eventCaller;
public FightManager(EventCaller eventCaller) {
this.eventCaller = eventCaller;
}
public boolean isInCombat(UUID player) {
if (!this.fights.containsKey(player)) {
@@ -19,25 +30,32 @@ public class FightManager {
FightTag fightTag = this.fights.get(player);
if (fightTag.isExpired()) {
this.fights.remove(player);
return false;
return !fightTag.isExpired();
}
public FightUntagEvent untag(UUID player, CauseOfUnTag causeOfUnTag) {
FightUntagEvent event = this.eventCaller.publishEvent(new FightUntagEvent(player, causeOfUnTag));
if (event.isCancelled()) {
return event;
}
return true;
}
public void untag(UUID player) {
this.fights.remove(player);
return event;
}
public void tag(UUID target, Duration delay) {
public FightTagEvent tag(UUID target, Duration delay, CauseOfTag causeOfTag) {
FightTagEvent event = this.eventCaller.publishEvent(new FightTagEvent(target, causeOfTag));
if (event.isCancelled()) {
return event;
}
Instant now = Instant.now();
Instant endOfCombatLog = now.plus(delay);
FightTag fightTag = new FightTag(target, endOfCombatLog);
this.fights.put(target, fightTag);
return event;
}
public Collection<FightTag> getFights() {
@@ -9,21 +9,20 @@ public class FightTag {
private final UUID taggedPlayer;
private final Instant endOfCombatLog;
private double healthBeforeDeath;
private FightDeathCause deathCause;
FightTag(UUID personToAddCombat, Instant endOfCombatLog) {
this.taggedPlayer = personToAddCombat;
this.endOfCombatLog = endOfCombatLog;
this.healthBeforeDeath = 0;
this.deathCause = FightDeathCause.UNKNOWN;
}
public UUID getTaggedPlayer() {
return this.taggedPlayer;
}
public Instant getEndOfCombatLog() {
return this.endOfCombatLog;
}
public boolean isExpired() {
return Instant.now().isAfter(this.endOfCombatLog);
}
@@ -38,19 +37,4 @@ public class FightTag {
return between;
}
public double getHealthBeforeDeath() {
return this.healthBeforeDeath;
}
public void setHealthBeforeDeath(double health) {
this.healthBeforeDeath = health;
}
public FightDeathCause getDeathCause() {
return this.deathCause;
}
public void setDeathCause(FightDeathCause deathCause) {
this.deathCause = deathCause;
}
}
@@ -0,0 +1,67 @@
package com.eternalcode.combat.fight;
import com.eternalcode.combat.fight.event.CauseOfUnTag;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.bossbar.FightBossBarService;
import com.eternalcode.combat.notification.Notification;
import com.eternalcode.combat.notification.NotificationAnnouncer;
import com.eternalcode.combat.notification.implementation.BossBarNotification;
import com.eternalcode.combat.util.DurationUtil;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import panda.utilities.text.Formatter;
import java.time.Duration;
import java.util.UUID;
public class FightTask implements Runnable {
private final Server server;
private final PluginConfig config;
private final FightManager fightManager;
private final FightBossBarService bossBarService;
private final NotificationAnnouncer announcer;
public FightTask(Server server, PluginConfig config, FightManager fightManager, FightBossBarService bossBarService, NotificationAnnouncer announcer) {
this.server = server;
this.config = config;
this.fightManager = fightManager;
this.bossBarService = bossBarService;
this.announcer = announcer;
}
@Override
public void run() {
for (FightTag fightTag : this.fightManager.getFights()) {
Player player = this.server.getPlayer(fightTag.getTaggedPlayer());
if (player == null) {
continue;
}
UUID playerUniqueId = player.getUniqueId();
if (fightTag.isExpired()) {
this.fightManager.untag(playerUniqueId, CauseOfUnTag.TIME_EXPIRED);
return;
}
Duration remaining = fightTag.getRemainingDuration();
Formatter formatter = new Formatter()
.register("{TIME}", DurationUtil.format(remaining));
Notification combatNotification = this.config.messages.combatNotification;
this.sendFightNotification(player, fightTag, combatNotification, formatter);
}
}
private void sendFightNotification(Player player, FightTag fightTag, Notification notification, Formatter formatter) {
if (notification instanceof BossBarNotification bossBarNotification) {
this.bossBarService.send(player, fightTag, bossBarNotification, formatter);
return;
}
this.announcer.send(player, notification, formatter);
}
}
@@ -0,0 +1,9 @@
package com.eternalcode.combat.fight.bossbar;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.bossbar.BossBar;
import java.time.Duration;
public record FightBossBar(Audience audience, BossBar bossBar, float progress, Duration combatDuration) {
}
@@ -0,0 +1,27 @@
package com.eternalcode.combat.fight.bossbar;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
class FightBossBarRegistry {
private final Map<UUID, FightBossBar> fightBossBars = new ConcurrentHashMap<>();
void add(UUID uuid, FightBossBar fightBossBar) {
this.fightBossBars.put(uuid, fightBossBar);
}
void remove(UUID uuid) {
this.fightBossBars.remove(uuid);
}
boolean hasFightBossBar(UUID uuid) {
return this.fightBossBars.containsKey(uuid);
}
Optional<FightBossBar> getFightBossBar(UUID uuid) {
return Optional.ofNullable(this.fightBossBars.get(uuid));
}
}
@@ -0,0 +1,119 @@
package com.eternalcode.combat.fight.bossbar;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightTag;
import com.eternalcode.combat.notification.implementation.BossBarNotification;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.platform.AudienceProvider;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
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;
public class FightBossBarService {
private final PluginConfig pluginConfig;
private final FightBossBarRegistry bossBarRegistry = new FightBossBarRegistry();
private final AudienceProvider audienceProvider;
private final MiniMessage miniMessage;
public FightBossBarService(PluginConfig pluginConfig, AudienceProvider audienceProvider, MiniMessage miniMessage) {
this.pluginConfig = pluginConfig;
this.audienceProvider = audienceProvider;
this.miniMessage = miniMessage;
}
public void hide(FightTag fightTag, FightBossBar fightBossBar) {
Audience audience = fightBossBar.audience();
BossBar bossBar = fightBossBar.bossBar();
UUID taggedPlayer = fightTag.getTaggedPlayer();
audience.hideBossBar(bossBar);
this.bossBarRegistry.remove(taggedPlayer);
}
public void hide(UUID playerUuid) {
Optional<FightBossBar> bossBar = this.bossBarRegistry.getFightBossBar(playerUuid);
if (bossBar.isPresent()) {
FightBossBar fightBossBar = bossBar.get();
Audience audience = fightBossBar.audience();
BossBar bar = fightBossBar.bossBar();
audience.hideBossBar(bar);
this.bossBarRegistry.remove(playerUuid);
}
}
public void send(Player player, FightTag fightTag, BossBarNotification bossBarNotification, Formatter formatter) {
UUID playerUniqueId = player.getUniqueId();
FightBossBar fightBossBar = this.bossBarRegistry.getFightBossBar(playerUniqueId)
.orElseGet(() -> this.create(player, bossBarNotification, formatter));
if (!this.bossBarRegistry.hasFightBossBar(playerUniqueId)) {
this.show(player, fightBossBar);
return;
}
String message = formatter.format(bossBarNotification.message());
this.update(fightTag, fightBossBar, message);
}
private void update(FightTag fightTag, FightBossBar fightBossBar, String message) {
if (fightTag.isExpired()) {
this.hide(fightTag, fightBossBar);
return;
}
BossBar bossBar = fightBossBar.bossBar();
float fightBossBarProgress = fightBossBar.progress();
if (fightBossBarProgress < 0.0F) {
Instant now = Instant.now();
Instant endOfCombatLog = fightTag.getEndOfCombatLog();
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);
}
Component name = this.miniMessage.deserialize(message);
bossBar.name(name);
}
private void show(Player player, FightBossBar fightBossBar) {
UUID playerUniqueId = player.getUniqueId();
Audience audience = this.audienceProvider.player(playerUniqueId);
BossBar bossBar = fightBossBar.bossBar();
audience.showBossBar(bossBar);
this.bossBarRegistry.add(playerUniqueId, fightBossBar);
}
private FightBossBar create(Player player, BossBarNotification bossBarNotification, Formatter formatter) {
Audience audience = this.audienceProvider.player(player.getUniqueId());
Component name = this.miniMessage.deserialize(formatter.format(bossBarNotification.message()));
BossBar bossBar = bossBarNotification.create(name);
float progress = bossBarNotification.progress();
Duration combatDuration = this.pluginConfig.settings.combatDuration;
return new FightBossBar(audience, bossBar, progress, combatDuration);
}
}
@@ -14,6 +14,7 @@ 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;
@@ -63,7 +64,7 @@ public class FightActionBlockerController implements Listener {
}
@EventHandler
public void onToggleGlide(EntityToggleGlideEvent event) {
void onToggleGlide(EntityToggleGlideEvent event) {
if (!this.config.settings.shouldPreventElytraUsage) {
return;
}
@@ -83,7 +84,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 +138,7 @@ public class FightActionBlockerController implements Listener {
}
@EventHandler
public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
Player player = event.getPlayer();
UUID playerUniqueId = player.getUniqueId();
@@ -0,0 +1,63 @@
package com.eternalcode.combat.fight.controller;
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.CauseOfUnTag;
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;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import panda.utilities.text.Formatter;
public class FightMessageController implements Listener {
private final FightManager fightManager;
private final NotificationAnnouncer announcer;
private final FightBossBarService bossBarService;
private final PluginConfig config;
private final Server server;
public FightMessageController(FightManager fightManager, NotificationAnnouncer announcer, FightBossBarService bossBarService, PluginConfig config, Server server) {
this.fightManager = fightManager;
this.announcer = announcer;
this.bossBarService = bossBarService;
this.config = config;
this.server = server;
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
void onTag(FightTagEvent 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.playerTagged);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
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());
}
}
@@ -3,7 +3,7 @@ package com.eternalcode.combat.fight.controller;
import com.eternalcode.combat.WhitelistBlacklistMode;
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;
@@ -22,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)
@@ -37,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;
}
@@ -46,27 +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();
if (!this.fightManager.isInCombat(attackedUniqueId)) {
this.announcer.sendMessage(personToAddCombatTime, this.config.messages.playerTagged);
if (attacker.isOp()) {
return;
}
if (!this.fightManager.isInCombat(personToAddCombatTimeUniqueId)) {
this.announcer.sendMessage(attackedPlayerByPerson, this.config.messages.playerTagged);
if (attackedPlayerByPerson.isOp() && this.config.settings.excludeAdminFromCombat) {
return;
}
this.fightManager.tag(attackedUniqueId, combatTime);
this.fightManager.tag(personToAddCombatTimeUniqueId, combatTime);
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);
this.fightManager.tag(attackerUniqueId, combatTime, CauseOfTag.PLAYER);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
@@ -98,11 +107,7 @@ public class FightTagController implements Listener {
return;
}
if (!this.fightManager.isInCombat(uuid)) {
this.announcer.sendMessage(player, this.config.messages.playerTagged);
}
this.fightManager.tag(uuid, combatTime);
this.fightManager.tag(uuid, combatTime, CauseOfTag.NON_PLAYER);
}
@Nullable
@@ -1,8 +1,9 @@
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.notification.NotificationAnnouncer;
import com.eternalcode.combat.fight.logout.LogoutService;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -12,12 +13,12 @@ public class FightUnTagController implements Listener {
private final FightManager fightManager;
private final PluginConfig config;
private final NotificationAnnouncer announcer;
private final LogoutService logoutService;
public FightUnTagController(FightManager fightManager, PluginConfig config, NotificationAnnouncer announcer) {
public FightUnTagController(FightManager fightManager, PluginConfig config, LogoutService logoutService) {
this.fightManager = fightManager;
this.config = config;
this.announcer = announcer;
this.logoutService = logoutService;
}
@EventHandler
@@ -29,12 +30,29 @@ public class FightUnTagController implements Listener {
return;
}
this.announcer.sendMessage(player, this.config.messages.playerUntagged);
this.fightManager.untag(player.getUniqueId());
CauseOfUnTag cause = this.getDeathCause(player, killer);
this.fightManager.untag(player.getUniqueId(), cause);
if (killer != null && this.config.settings.shouldReleaseAttacker) {
this.fightManager.untag(killer.getUniqueId());
this.announcer.sendMessage(killer, this.config.messages.playerUntagged);
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;
}
}
@@ -0,0 +1,124 @@
package com.eternalcode.combat.fight.effect;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.event.CauseOfUnTag;
import com.eternalcode.combat.fight.event.FightTagEvent;
import com.eternalcode.combat.fight.event.FightUntagEvent;
import org.bukkit.Bukkit;
import org.bukkit.Server;
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.EntityPotionEffectEvent;
import org.bukkit.potion.PotionEffect;
public class FightEffectController implements Listener {
private final FightEffectService effectService;
private final FightEffectSettings effectSettings;
private final FightManager fightManager;
private final Server server;
public FightEffectController(FightEffectSettings settings, FightEffectService effectService, FightManager fightManager, Server server) {
this.effectSettings = settings;
this.effectService = effectService;
this.fightManager = fightManager;
this.server = server;
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onTag(FightTagEvent event) {
if (!this.effectSettings.customEffectsEnabled) {
return;
}
Player player = this.server.getPlayer(event.getPlayer());
if (player == null) {
return;
}
this.effectSettings.customEffects.forEach((key, value) ->
this.effectService.applyCustomEffect(player, key, value));
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onQuit(FightUntagEvent event) {
if (!this.effectSettings.customEffectsEnabled) {
return;
}
if (event.getCause() == CauseOfUnTag.LOGOUT) {
Player player = Bukkit.getPlayer(event.getPlayer());
assert player != null;
this.effectService.clearStoredEffects(player);
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onUntag(FightUntagEvent event) {
if (!this.effectSettings.customEffectsEnabled) {
return;
}
Player player = this.server.getPlayer(event.getPlayer());
if (player == null) {
return;
}
this.effectSettings.customEffects.forEach((key, value) -> this.effectService.removeCustomEffect(player, key, value));
this.effectService.restoreActiveEffects(player);
}
@EventHandler
public void onDeath(FightUntagEvent event) {
if (!this.effectSettings.customEffectsEnabled) {
return;
}
if (event.getCause() == CauseOfUnTag.DEATH_BY_PLAYER || event.getCause() == CauseOfUnTag.DEATH) {
Player player = Bukkit.getPlayer(event.getPlayer());
assert player != null;
this.effectService.clearStoredEffects(player);
}
}
@EventHandler
public void onEffectChange(EntityPotionEffectEvent event) {
if (!this.effectSettings.customEffectsEnabled) {
return;
}
if (!(event.getEntity() instanceof Player player)) {
return;
}
if (!this.fightManager.isInCombat(player.getUniqueId())) {
return;
}
PotionEffect newEffect = event.getNewEffect();
PotionEffect oldEffect = event.getOldEffect();
if (!this.isRemovedEffect(newEffect, oldEffect)) {
return;
}
Integer customAmplifier = this.effectSettings.customEffects.get(oldEffect.getType());
if (customAmplifier == null) {
return;
}
player.addPotionEffect(new PotionEffect(oldEffect.getType(), -1, customAmplifier));
}
private boolean isRemovedEffect(PotionEffect newEffect, PotionEffect oldEffect) {
return newEffect == null && oldEffect != null;
}
}
@@ -0,0 +1,75 @@
package com.eternalcode.combat.fight.effect;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.List;
import java.util.ArrayList;
public class FightEffectService {
private final Map<UUID, List<PotionEffect>> activeEffects = new HashMap<>();
private static final int INFINITE_DURATION = -1;
public void storeActiveEffect(Player player, PotionEffect effect) {
List<PotionEffect> effects = this.activeEffects.computeIfAbsent(player.getUniqueId(), k -> new ArrayList<>());
effects.add(effect);
}
public void restoreActiveEffects(Player player) {
List<PotionEffect> currentEffects = this.getCurrentEffects(player);
for (PotionEffect effect : currentEffects) {
player.addPotionEffect(effect);
}
this.clearStoredEffects(player);
}
public void clearStoredEffects(Player player) {
this.activeEffects.remove(player.getUniqueId());
}
public List<PotionEffect> getCurrentEffects(Player player) {
return this.activeEffects.getOrDefault(player.getUniqueId(), new ArrayList<>());
}
public void applyCustomEffect(Player player, PotionEffectType type, Integer amplifier) {
PotionEffect activeEffect = player.getPotionEffect(type);
if (activeEffect == null) {
player.addPotionEffect(new PotionEffect(type, INFINITE_DURATION, amplifier));
return;
}
if (activeEffect.getAmplifier() > amplifier) {
return;
}
if (activeEffect.getDuration() == -1) {
return;
}
this.storeActiveEffect(player, activeEffect);
player.addPotionEffect(new PotionEffect(type, INFINITE_DURATION, amplifier));
}
public void removeCustomEffect(Player player, PotionEffectType type, Integer amplifier) {
PotionEffect activeEffect = player.getPotionEffect(type);
if (activeEffect == null) {
return;
}
if (activeEffect.getAmplifier() != amplifier) {
return;
}
player.removePotionEffect(type);
}
}
@@ -0,0 +1,26 @@
package com.eternalcode.combat.fight.effect;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment;
import org.bukkit.potion.PotionEffectType;
import java.util.Map;
public class FightEffectSettings extends OkaeriConfig {
@Comment({"# Do you want to add effects to players in combat?"})
public boolean customEffectsEnabled = false;
@Comment({
"# If the option above is set to true, you can add effects to players in combat below",
"# You can find a list of all potion effects here: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/potion/PotionEffectType.html",
"# Correct format: 'EFFECT_TYPE:AMPLIFIER' Amplifier strength starts from 0, so level 1 gives effect strength 2",
"# Example: SPEED:1, DAMAGE_RESISTANCE:0",
})
public Map<PotionEffectType, Integer> customEffects = Map.of(
PotionEffectType.SPEED, 1,
PotionEffectType.DAMAGE_RESISTANCE, 0
);
}
@@ -0,0 +1,8 @@
package com.eternalcode.combat.fight.event;
public enum CauseOfTag {
PLAYER,
NON_PLAYER,
COMMAND,
CUSTOM
}
@@ -0,0 +1,11 @@
package com.eternalcode.combat.fight.event;
public enum CauseOfUnTag {
TIME_EXPIRED,
DEATH,
DEATH_BY_PLAYER,
LOGOUT,
COMMAND,
ATTACKER_RELEASE,
CUSTOM
}
@@ -0,0 +1,63 @@
package com.eternalcode.combat.fight.event;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import java.util.UUID;
public class FightTagEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final UUID player;
private final CauseOfTag cause;
private boolean isCancelled = false;
private String cancelMessage = "";
public FightTagEvent(UUID player, CauseOfTag cause) {
this.player = player;
this.cause = cause;
}
public UUID getPlayer() {
return this.player;
}
public CauseOfTag getCause() {
return this.cause;
}
@Override
public boolean isCancelled() {
return this.isCancelled;
}
@Override
public void setCancelled(boolean cancelled) {
throw new UnsupportedOperationException("Use #cancel(String) instead");
}
public void cancel(String cancelMessage) {
this.isCancelled = true;
this.cancelMessage = cancelMessage;
}
public void allow() {
this.isCancelled = false;
this.cancelMessage = "";
}
public String getCancelMessage() {
return this.cancelMessage;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}
@@ -0,0 +1,47 @@
package com.eternalcode.combat.fight.event;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import java.util.UUID;
public class FightUntagEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final UUID player;
private final CauseOfUnTag cause;
private boolean isCancelled = false;
public FightUntagEvent(UUID player, CauseOfUnTag cause) {
this.player = player;
this.cause = cause;
}
public UUID getPlayer() {
return this.player;
}
public CauseOfUnTag getCause() {
return this.cause;
}
@Override
public boolean isCancelled() {
return this.isCancelled;
}
@Override
public void setCancelled(boolean cancelled) {
this.isCancelled = cancelled;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}
@@ -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);
this.announcer.broadcast(player, 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();
}
@@ -1,30 +1,29 @@
package com.eternalcode.combat.fight.pearl;
import net.dzikoysk.cdn.entity.Contextual;
import net.dzikoysk.cdn.entity.Description;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment;
import java.time.Duration;
@Contextual
public class FightPearlSettings {
public class FightPearlSettings extends OkaeriConfig {
@Description({ "# Is pearl damage to be enabled?", "# This will work globally" })
@Comment({ "# Is pearl damage to be enabled?", "# This will work globally" })
public boolean pearlThrowDamageEnabled = true;
@Description("# Set true, If you want to lock pearls during the combat")
@Comment("# Set true, If you want to lock pearls during the combat")
public boolean pearlThrowBlocked = false;
@Description({
@Comment({
"# Block throwing pearls with delay?",
"# If you set this to for example 3s, player will have to wait 3 seconds before throwing another pearl",
"# Set to 0 to disable"
})
public Duration pearlThrowDelay = Duration.ofSeconds(3);
@Description("# Message sent when player tries to throw ender pearl, but are disabled")
@Comment("# Message sent when player tries to throw ender pearl, but are disabled")
public String pearlThrowBlockedDuringCombat = "&cThrowing ender pearls is prohibited during combat!";
@Description("# Message sent when player tries to throw ender pearl, but has delay")
@Comment("# Message sent when player tries to throw ender pearl, but has delay")
public String pearlThrowBlockedDelayDuringCombat = "&cYou must wait {TIME} before next throw!";
}
@@ -0,0 +1,29 @@
package com.eternalcode.combat.fight.tagout;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.event.FightTagEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import java.util.UUID;
public class FightTagOutController implements Listener {
private final FightTagOutService tagOutService;
private final PluginConfig config;
public FightTagOutController(FightTagOutService tagOutService, PluginConfig config) {
this.tagOutService = tagOutService;
this.config = config;
}
@EventHandler
void onTagOut(FightTagEvent event) {
UUID uniqueId = event.getPlayer();
if (this.tagOutService.isTaggedOut(uniqueId)) {
event.cancel(this.config.messages.admin.adminTagOutCanceled);
}
}
}
@@ -0,0 +1,34 @@
package com.eternalcode.combat.fight.tagout;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class FightTagOutService {
private final Map<UUID, Instant> tagOuts = new HashMap<>();
public void tagOut(UUID player, Duration duration) {
Instant endTime = Instant.now().plus(duration);
this.tagOuts.put(player, endTime);
}
public void unTagOut(UUID player) {
this.tagOuts.remove(player);
}
public boolean isTaggedOut(UUID player) {
Instant endTime = this.tagOuts.get(player);
if (endTime == null) {
return false;
}
Instant now = Instant.now();
return now.isBefore(endTime);
}
}
@@ -0,0 +1,94 @@
package com.eternalcode.combat.fight.tagout;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.notification.NotificationAnnouncer;
import com.eternalcode.combat.util.DurationUtil;
import dev.rollczi.litecommands.argument.Arg;
import dev.rollczi.litecommands.command.execute.Execute;
import dev.rollczi.litecommands.command.permission.Permission;
import dev.rollczi.litecommands.command.route.Route;
import org.bukkit.entity.Player;
import panda.utilities.text.Formatter;
import java.time.Duration;
import java.time.Instant;
import java.util.UUID;
@Route(name = "tagout", aliases = "tagimmunity")
public class TagOutCommand {
private final FightTagOutService fightTagOutService;
private final NotificationAnnouncer announcer;
private final PluginConfig config;
public TagOutCommand(FightTagOutService fightTagOutService, NotificationAnnouncer announcer, PluginConfig config) {
this.fightTagOutService = fightTagOutService;
this.announcer = announcer;
this.config = config;
}
@Execute(required = 1)
@Permission("eternalcombat.tagout")
void tagout(Player sender, @Arg Duration time) {
UUID targetUniqueId = sender.getUniqueId();
Formatter formatter = new Formatter()
.register("{PLAYER}", sender.getName())
.register("{TIME}", DurationUtil.format(time));
this.fightTagOutService.tagOut(targetUniqueId, time);
String format = formatter.format(this.config.messages.admin.adminTagOutSelf);
this.announcer.sendMessage(sender, format);
}
@Execute(required = 2)
@Permission("eternalcombat.tagout")
void tagout(Player sender, @Arg Player target, @Arg Duration time) {
UUID targetUniqueId = target.getUniqueId();
Instant now = Instant.now();
Duration remaining = Duration.between(now, now.plus(time));
Formatter formatter = new Formatter()
.register("{PLAYER}", target.getName())
.register("{TIME}", DurationUtil.format(remaining));
this.fightTagOutService.tagOut(targetUniqueId, time);
String adminTagOutFormat = formatter.format(this.config.messages.admin.adminTagOut);
this.announcer.sendMessage(sender, adminTagOutFormat);
String playerTagOutFormat = formatter.format(this.config.messages.admin.playerTagOut);
this.announcer.sendMessage(target, playerTagOutFormat);
}
@Execute(route = "remove", required = 1)
@Permission("eternalcombat.tagout")
void untagout(Player sender, @Arg Player target) {
UUID targetUniqueId = target.getUniqueId();
this.fightTagOutService.unTagOut(targetUniqueId);
Formatter formatter = new Formatter()
.register("{PLAYER}", target.getName());
if (!targetUniqueId.equals(sender.getUniqueId())) {
String adminUnTagOutFormat = formatter.format(this.config.messages.admin.adminTagOutOff);
this.announcer.sendMessage(sender, adminUnTagOutFormat);
}
this.announcer.sendMessage(target, this.config.messages.admin.playerTagOutOff);
}
@Execute(route = "remove", required = 0)
@Permission("eternalcombat.tagout")
void untagout(Player sender) {
UUID senderUniqueId = sender.getUniqueId();
this.fightTagOutService.unTagOut(senderUniqueId);
this.announcer.sendMessage(sender, this.config.messages.admin.playerTagOutOff);
}
}
@@ -0,0 +1,8 @@
package com.eternalcode.combat.notification;
public interface Notification {
String message();
NotificationType type();
}
@@ -0,0 +1,79 @@
package com.eternalcode.combat.notification;
import com.eternalcode.combat.notification.implementation.BossBarNotification;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.platform.AudienceProvider;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.title.Title;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import panda.utilities.text.Formatter;
public final class NotificationAnnouncer {
private final AudienceProvider audienceProvider;
private final MiniMessage miniMessage;
public NotificationAnnouncer(AudienceProvider audienceProvider, MiniMessage miniMessage) {
this.audienceProvider = audienceProvider;
this.miniMessage = miniMessage;
}
public void send(CommandSender sender, Notification notification, Formatter formatter) {
Audience audience = this.audience(sender);
Component message = this.miniMessage.deserialize(formatter.format(notification.message()));
NotificationType type = notification.type();
switch (type) {
case CHAT -> audience.sendMessage(message);
case ACTION_BAR -> audience.sendActionBar(message);
case TITLE -> {
Title title = Title.title(message, Component.empty());
audience.showTitle(title);
}
case SUB_TITLE -> {
Title subTitle = Title.title(Component.empty(), message);
audience.showTitle(subTitle);
}
case BOSS_BAR -> {
BossBarNotification bossBarNotification = (BossBarNotification) notification;
BossBar bossBar = bossBarNotification.create(message);
audience.showBossBar(bossBar);
}
default -> throw new IllegalStateException("Unknown notification type: " + type);
}
}
public void sendMessage(CommandSender commandSender, String text) {
Audience audience = this.audience(commandSender);
Component message = this.miniMessage.deserialize(text);
audience.sendMessage(message);
}
public void broadcast(String text) {
Audience audience = this.audienceProvider.all();
Component message = this.miniMessage.deserialize(text);
audience.sendMessage(message);
}
private Audience audience(CommandSender sender) {
if (sender instanceof Player player) {
return this.audienceProvider.player(player.getUniqueId());
}
return this.audienceProvider.console();
}
}
@@ -1,9 +1,9 @@
package com.eternalcode.combat.notification;
public enum NotificationType {
ACTION_BAR,
CHAT,
ACTION_BAR,
TITLE,
SUBTITLE,
SUB_TITLE,
BOSS_BAR
}
@@ -0,0 +1,12 @@
package com.eternalcode.combat.notification.implementation;
import com.eternalcode.combat.notification.Notification;
import com.eternalcode.combat.notification.NotificationType;
public record ActionBarNotification(String message) implements Notification {
@Override
public NotificationType type() {
return NotificationType.ACTION_BAR;
}
}
@@ -0,0 +1,20 @@
package com.eternalcode.combat.notification.implementation;
import com.eternalcode.combat.notification.Notification;
import com.eternalcode.combat.notification.NotificationType;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
public record BossBarNotification(String message, float progress, BossBar.Color color, BossBar.Overlay overlay) implements Notification {
public BossBar create(Component name) {
float replacedProgress = this.progress < 0.0F ? BossBar.MAX_PROGRESS : this.progress;
return BossBar.bossBar(name, replacedProgress, this.color, this.overlay);
}
@Override
public NotificationType type() {
return NotificationType.BOSS_BAR;
}
}
@@ -0,0 +1,12 @@
package com.eternalcode.combat.notification.implementation;
import com.eternalcode.combat.notification.Notification;
import com.eternalcode.combat.notification.NotificationType;
public record ChatNotification(String message) implements Notification {
@Override
public NotificationType type() {
return NotificationType.CHAT;
}
}
@@ -0,0 +1,12 @@
package com.eternalcode.combat.notification.implementation;
import com.eternalcode.combat.notification.Notification;
import com.eternalcode.combat.notification.NotificationType;
public record SubTitleNotification(String message) implements Notification {
@Override
public NotificationType type() {
return NotificationType.SUB_TITLE;
}
}
@@ -0,0 +1,12 @@
package com.eternalcode.combat.notification.implementation;
import com.eternalcode.combat.notification.Notification;
import com.eternalcode.combat.notification.NotificationType;
public record TitleNotification(String message) implements Notification {
@Override
public NotificationType type() {
return NotificationType.TITLE;
}
}
@@ -0,0 +1,64 @@
package com.eternalcode.combat.notification.serializer;
import com.eternalcode.combat.notification.Notification;
import com.eternalcode.combat.notification.NotificationType;
import com.eternalcode.combat.notification.implementation.ActionBarNotification;
import com.eternalcode.combat.notification.implementation.BossBarNotification;
import com.eternalcode.combat.notification.implementation.ChatNotification;
import com.eternalcode.combat.notification.implementation.SubTitleNotification;
import com.eternalcode.combat.notification.implementation.TitleNotification;
import eu.okaeri.configs.schema.GenericsDeclaration;
import eu.okaeri.configs.serdes.DeserializationData;
import eu.okaeri.configs.serdes.ObjectSerializer;
import eu.okaeri.configs.serdes.SerializationData;
import net.kyori.adventure.bossbar.BossBar;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Optional;
public class NotificationSerializer implements ObjectSerializer<Notification> {
@Override
public boolean supports(@NonNull Class<? super Notification> type) {
return Notification.class.isAssignableFrom(type);
}
@Override
public void serialize(@NonNull Notification notification, @NonNull SerializationData data, @NonNull GenericsDeclaration generics) {
data.add("type", notification.type(), NotificationType.class);
data.add("message", notification.message(), String.class);
if (notification instanceof BossBarNotification bossBarNotification) {
data.add("progress", bossBarNotification.progress(), float.class);
data.add("color", bossBarNotification.color(), BossBar.Color.class);
data.add("overlay", bossBarNotification.overlay(), BossBar.Overlay.class);
}
}
@Override
public Notification deserialize(@NonNull DeserializationData data, @NonNull GenericsDeclaration generics) {
NotificationType type = data.get("type", NotificationType.class);
String message = data.get("message", String.class);
return switch (type) {
case CHAT -> new ChatNotification(message);
case ACTION_BAR -> new ActionBarNotification(message);
case TITLE -> new TitleNotification(message);
case SUB_TITLE -> new SubTitleNotification(message);
case BOSS_BAR -> {
float progress = Optional.ofNullable(data.get("progress", float.class))
.orElse(BossBar.MAX_PROGRESS);
BossBar.Color color = Optional.ofNullable(data.get("color", BossBar.Color.class))
.orElse(BossBar.Color.RED);
BossBar.Overlay overlay = Optional.ofNullable(data.get("overlay", BossBar.Overlay.class))
.orElse(BossBar.Overlay.PROGRESS);
yield new BossBarNotification(message, progress, color, overlay);
}
};
}
}
@@ -22,6 +22,15 @@ public class DefaultRegionProvider implements RegionProvider {
return this.contains(min, max, location.getX(), location.getZ());
}
@Override
public Location getRegionCenter(Location location) {
Location spawnLocation = location.getWorld().getSpawnLocation();
double x = spawnLocation.getX();
double z = spawnLocation.getZ();
return new Location(location.getWorld(), x, location.getY(), z);
}
public boolean contains(Point min, Point max, double x, double z) {
return x >= min.x() && x < max.x()
&& z >= min.z() && z < max.z();
@@ -8,6 +8,7 @@ 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 {
@@ -47,8 +48,9 @@ public class RegionController implements Listener {
return;
}
Location playerLocation = player.getLocation().subtract(player.getWorld().getSpawnLocation());
double distance = playerLocation.distance(player.getWorld().getSpawnLocation());
Location spawnLocation = this.regionProvider.getRegionCenter(locationTo);
Location playerLocation = player.getLocation().subtract(spawnLocation);
double distance = playerLocation.distance(spawnLocation);
Vector knockback = new Vector(0, 3, 0).multiply(this.pluginConfig.settings.blockedRegionKnockMultiplier / distance);
Vector vector = playerLocation.toVector().add(knockback);
@@ -57,4 +59,20 @@ public class RegionController implements Listener {
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);
}
}
}
@@ -2,9 +2,10 @@ package com.eternalcode.combat.region;
import org.bukkit.Location;
@FunctionalInterface
public interface RegionProvider {
boolean isInRegion(Location location);
Location getRegionCenter(Location location);
}
@@ -0,0 +1,69 @@
package com.eternalcode.combat.region;
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.regions.ProtectedRegion;
import com.sk89q.worldguard.protection.regions.RegionContainer;
import com.sk89q.worldguard.protection.regions.RegionQuery;
import org.bukkit.Location;
import java.util.List;
public class WorldGuardRegionProvider implements RegionProvider {
private final List<String> regions;
public WorldGuardRegionProvider(List<String> regions) {
this.regions = regions;
}
@Override
public boolean isInRegion(Location location) {
return this.regions.stream().anyMatch(region -> this.isInCombatRegion(location, region));
}
@Override
public Location getRegionCenter(Location location) {
RegionContainer regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer();
RegionQuery regionQuery = regionContainer.createQuery();
ApplicableRegionSet applicableRegions = regionQuery.getApplicableRegions(BukkitAdapter.adapt(location));
double minX = 0;
double maxX = 0;
double minZ = 0;
double maxZ = 0;
for (ProtectedRegion region : applicableRegions.getRegions()) {
BlockVector3 min = region.getMinimumPoint();
BlockVector3 max = region.getMaximumPoint();
if (min.getX() < minX) {
minX = min.getX();
}
if (max.getX() > maxX) {
maxX = max.getX();
}
if (min.getZ() < minZ) {
minZ = min.getZ();
}
if (max.getZ() > maxZ) {
maxZ = max.getZ();
}
}
double x = (maxX - minX) / 2 + minX;
double z = (maxZ - minZ) / 2 + minZ;
return new Location(location.getWorld(), x, location.getY(), z);
}
private boolean isInCombatRegion(Location location, String regionName) {
RegionContainer regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer();
RegionQuery regionQuery = regionContainer.createQuery();
ApplicableRegionSet applicableRegions = regionQuery.getApplicableRegions(BukkitAdapter.adapt(location));
return applicableRegions.getRegions().stream().anyMatch(region -> region.getId().equalsIgnoreCase(regionName));
}
}
@@ -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;
}
@@ -0,0 +1,24 @@
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 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 WITHOUT_MILLS.format(duration);
}
}
@@ -0,0 +1,51 @@
package com.eternalcode.combat.util;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import java.util.function.ToIntFunction;
public class InventoryUtil {
private static final Random RANDOM = new Random();
private InventoryUtil() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
public static <T> int calculateItemsToDelete(int percent, Collection<T> objectList, ToIntFunction<? super T> mapper) {
return MathUtil.getRoundedCountFromPercentage(percent, MathUtil.sum(objectList, mapper));
}
public static RemoveItemResult removeRandomItems(List<ItemStack> list, int itemsToDelete) {
List<ItemStack> currentItems = new ArrayList<>(list);
List<ItemStack> removedItems = new ArrayList<>();
int currentItemsToDelete = itemsToDelete;
while (currentItemsToDelete > 0) {
int randomIndex = RANDOM.nextInt(list.size());
ItemStack currentItem = currentItems.get(randomIndex);
int amount = currentItem.getAmount();
int randomAmount = RANDOM.nextInt(0, Math.min(currentItemsToDelete, amount) + 1);
if (amount <= 0 || randomAmount <= 0) {
continue;
}
ItemStack removedItem = currentItem.clone();
removedItem.setAmount(randomAmount);
removedItems.add(removedItem);
currentItem.setAmount(amount - randomAmount);
currentItemsToDelete -= randomAmount;
}
return new RemoveItemResult(currentItems, removedItems);
}
}
@@ -0,0 +1,22 @@
package com.eternalcode.combat.util;
import org.bukkit.inventory.ItemStack;
import java.util.Collections;
import java.util.List;
public record RemoveItemResult(
List<ItemStack> restItems,
List<ItemStack> removedItems
) {
public List<ItemStack> restItems() {
return Collections.unmodifiableList(this.restItems);
}
@Override
public List<ItemStack> removedItems() {
return Collections.unmodifiableList(this.removedItems);
}
}
+63
View File
@@ -0,0 +1,63 @@
plugins {
`eternalcombat-java`
`eternalcombat-repositories`
id("net.minecrell.plugin-yml.bukkit")
id("com.github.johnrengelman.shadow")
id("xyz.jpenilla.run-paper")
}
dependencies {
implementation(project(":eternalcombat-api"))
}
bukkit {
main = "com.eternalcode.combat.CombatPlugin"
author = "EternalCodeTeam"
apiVersion = "1.13"
prefix = "EternalCombat"
name = "EternalCombat"
softDepend = listOf("WorldGuard")
version = "${project.version}"
}
tasks {
runServer {
minecraftVersion("1.19.4")
}
}
tasks.shadowJar {
archiveFileName.set("EternalCombat v${project.version}.jar")
dependsOn("checkstyleMain")
dependsOn("checkstyleTest")
dependsOn("test")
exclude(
"org/intellij/lang/annotations/**",
"org/jetbrains/annotations/**",
"META-INF/**",
"kotlin/**",
"javax/**"
)
val prefix = "com.eternalcode.combat.libs"
listOf(
"panda.std",
"panda.utilities",
"org.panda-lang",
"eu.okaeri",
"net.kyori",
"org.bstats",
"dev.rollczi.litecommands",
"com.eternalcode.gitcheck",
"org.json.simple",
"org.apache.commons",
"javassist",
"com.github.benmanes.caffeine",
"com.eternalcode.commons"
).forEach { pack ->
relocate(pack, "$prefix.$pack")
}
}
@@ -0,0 +1,210 @@
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.ConfigService;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.drop.DropController;
import com.eternalcode.combat.drop.DropKeepInventoryManager;
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.FightMessageController;
import com.eternalcode.combat.fight.controller.FightTagController;
import com.eternalcode.combat.fight.controller.FightUnTagController;
import com.eternalcode.combat.fight.effect.FightEffectController;
import com.eternalcode.combat.event.EventCaller;
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;
import com.eternalcode.combat.fight.tagout.FightTagOutService;
import com.eternalcode.combat.fight.tagout.TagOutCommand;
import com.eternalcode.combat.notification.NotificationAnnouncer;
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.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;
import dev.rollczi.litecommands.bukkit.tools.BukkitOnlyPlayerContextual;
import dev.rollczi.litecommands.bukkit.tools.BukkitPlayerArgument;
import net.kyori.adventure.platform.AudienceProvider;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bstats.bukkit.Metrics;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.util.concurrent.TimeUnit;
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;
private AudienceProvider audienceProvider;
private LiteCommands<CommandSender> liteCommands;
@Override
public void onEnable() {
Stopwatch started = Stopwatch.createStarted();
Server server = this.getServer();
File dataFolder = this.getDataFolder();
ConfigService configService = new ConfigService();
EventCaller eventCaller = new EventCaller(server);
this.pluginConfig = configService.create(PluginConfig.class, new File(dataFolder, "config.yml"));
this.fightManager = new FightManager(eventCaller);
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();
UpdaterService updaterService = new UpdaterService(this.getDescription());
this.audienceProvider = BukkitAudiences.create(this);
MiniMessage miniMessage = MiniMessage.builder()
.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();
this.regionProvider = bridgeService.getRegionProvider();
NotificationAnnouncer notificationAnnouncer = new NotificationAnnouncer(this.audienceProvider, miniMessage);
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))
.invalidUsageHandler(new InvalidUsage(this.pluginConfig, notificationAnnouncer))
.permissionHandler(new PermissionMessage(this.pluginConfig, notificationAnnouncer))
.commandInstance(new CombatCommand(this.fightManager, configService, notificationAnnouncer, this.pluginConfig))
.commandInstance(new TagOutCommand(this.fightTagOutService, notificationAnnouncer, this.pluginConfig))
.register();
FightTask fightTask = new FightTask(server, this.pluginConfig, this.fightManager, fightBossBarService, notificationAnnouncer);
this.getServer().getScheduler().runTaskTimer(this, fightTask, 20L, 20L);
new Metrics(this, BSTATS_METRICS_ID);
Stream.of(
new PercentDropModifier(this.pluginConfig.dropSettings),
new PlayersHealthDropModifier(this.pluginConfig.dropSettings, this.logoutService)
).forEach(this.dropManager::registerModifier);
Stream.of(
new DropController(this.dropManager, this.dropKeepInventoryManager, this.pluginConfig.dropSettings, this.fightManager),
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),
new RegionController(notificationAnnouncer, this.regionProvider, this.fightManager, this.pluginConfig),
new FightEffectController(this.pluginConfig.effect, this.fightEffectService, this.fightManager, this.getServer()),
new FightTagOutController(this.fightTagOutService, this.pluginConfig),
new FightMessageController(this.fightManager, notificationAnnouncer, fightBossBarService, this.pluginConfig, this.getServer())
).forEach(listener -> this.getServer().getPluginManager().registerEvents(listener, this));
EternalCombatProvider.initialize(this);
long millis = started.elapsed(TimeUnit.MILLISECONDS);
this.getLogger().info("Successfully loaded EternalCombat in " + millis + "ms");
}
@Override
public void onDisable() {
EternalCombatProvider.deinitialize();
if (this.liteCommands != null) {
this.liteCommands.getPlatform().unregisterAll();
}
if (this.audienceProvider != null) {
this.audienceProvider.close();
}
this.fightManager.untagAll();
}
@Override
public FightManager getFightManager() {
return this.fightManager;
}
@Override
public RegionProvider getRegionProvider() {
return this.regionProvider;
}
@Override
public FightPearlManager getFightPearlManager() {
return this.fightPearlManager;
}
@Override
public FightTagOutService getFightTagOutService() {
return this.fightTagOutService;
}
@Override
public FightEffectService getFightEffectService() {
return this.fightEffectService;
}
@Override
public DropManager getDropManager() {
return this.dropManager;
}
@Override
public DropKeepInventoryManager getDropKeepInventoryManager() {
return this.dropKeepInventoryManager;
}
@Override
public PluginConfig getPluginConfig() {
return this.pluginConfig;
}
}
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.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+8 -8
View File
@@ -55,7 +55,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/.
@@ -145,7 +145,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 +153,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 +202,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
+10 -10
View File
@@ -43,11 +43,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 +57,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
}
+2
View File
@@ -1 +1,3 @@
rootProject.name = "EternalCombat"
include("eternalcombat-api")
include("eternalcombat-plugin")
@@ -1,128 +0,0 @@
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.ConfigManager;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.drop.DropController;
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.FightManager;
import com.eternalcode.combat.fight.FightTask;
import com.eternalcode.combat.fight.controller.FightDeathCauseController;
import com.eternalcode.combat.fight.controller.FightEscapeController;
import com.eternalcode.combat.fight.controller.FightTagController;
import com.eternalcode.combat.fight.controller.FightUnTagController;
import com.eternalcode.combat.fight.controller.FightActionBlockerController;
import com.eternalcode.combat.fight.pearl.FightPearlController;
import com.eternalcode.combat.fight.pearl.FightPearlManager;
import com.eternalcode.combat.notification.NotificationAnnouncer;
import com.eternalcode.combat.region.RegionController;
import com.eternalcode.combat.updater.UpdaterNotificationController;
import com.eternalcode.combat.updater.UpdaterService;
import com.eternalcode.combat.util.legacy.LegacyColorProcessor;
import com.google.common.base.Stopwatch;
import dev.rollczi.litecommands.LiteCommands;
import dev.rollczi.litecommands.bukkit.adventure.platform.LiteBukkitAdventurePlatformFactory;
import dev.rollczi.litecommands.bukkit.tools.BukkitOnlyPlayerContextual;
import dev.rollczi.litecommands.bukkit.tools.BukkitPlayerArgument;
import net.kyori.adventure.platform.AudienceProvider;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bstats.bukkit.Metrics;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
public final class CombatPlugin extends JavaPlugin {
private FightManager fightManager;
private FightPearlManager fightPearlManager;
private AudienceProvider audienceProvider;
private LiteCommands<CommandSender> liteCommands;
@Override
public void onEnable() {
Stopwatch started = Stopwatch.createStarted();
Server server = this.getServer();
File dataFolder = this.getDataFolder();
ConfigBackupService backupService = new ConfigBackupService(dataFolder);
ConfigManager configManager = new ConfigManager(backupService, dataFolder);
PluginConfig pluginConfig = configManager.load(new PluginConfig());
this.fightManager = new FightManager();
this.fightPearlManager = new FightPearlManager(pluginConfig.pearl);
UpdaterService updaterService = new UpdaterService(this.getDescription());
this.audienceProvider = BukkitAudiences.create(this);
MiniMessage miniMessage = MiniMessage.builder()
.postProcessor(new LegacyColorProcessor())
.build();
BridgeService bridgeService = new BridgeService(pluginConfig, server.getPluginManager(), this.getLogger());
bridgeService.init();
NotificationAnnouncer notificationAnnouncer = new NotificationAnnouncer(this.audienceProvider, miniMessage);
this.liteCommands = LiteBukkitAdventurePlatformFactory.builder(server, "eternalcombat", this.audienceProvider)
.argument(Player.class, new BukkitPlayerArgument<>(this.getServer(), pluginConfig.messages.playerNotFound))
.contextualBind(Player.class, new BukkitOnlyPlayerContextual<>(pluginConfig.messages.admin.onlyForPlayers))
.invalidUsageHandler(new InvalidUsage(pluginConfig, notificationAnnouncer))
.permissionHandler(new PermissionMessage(pluginConfig, notificationAnnouncer))
.commandInstance(new CombatCommand(this.fightManager, configManager, notificationAnnouncer, pluginConfig))
.register();
FightTask fightTask = new FightTask(this.fightManager, pluginConfig, server, notificationAnnouncer);
this.getServer().getScheduler().runTaskTimerAsynchronously(this, fightTask, 20L, 20L);
new Metrics(this, 17803);
DropManager dropManager = new DropManager();
Stream.of(
new PercentDropModifier(pluginConfig.dropSettings),
new PlayersHealthDropModifier(pluginConfig.dropSettings)
).forEach(dropManager::registerModifier);
Stream.of(
new FightDeathCauseController(this.fightManager),
new DropController(dropManager, pluginConfig.dropSettings, this.fightManager),
new FightTagController(this.fightManager, pluginConfig, notificationAnnouncer),
new FightUnTagController(this.fightManager, pluginConfig, notificationAnnouncer),
new FightEscapeController(this.fightManager, pluginConfig, notificationAnnouncer),
new FightActionBlockerController(this.fightManager, notificationAnnouncer, pluginConfig),
new FightPearlController(pluginConfig.pearl, notificationAnnouncer, this.fightManager, this.fightPearlManager),
new UpdaterNotificationController(updaterService, pluginConfig, this.audienceProvider, miniMessage),
new RegionController(notificationAnnouncer, bridgeService.getRegionProvider(), this.fightManager, pluginConfig)
).forEach(listener -> this.getServer().getPluginManager().registerEvents(listener, this));
long millis = started.elapsed(TimeUnit.MILLISECONDS);
this.getLogger().info("Successfully loaded EternalCombat in " + millis + "ms");
}
@Override
public void onDisable() {
if (this.liteCommands != null) {
this.liteCommands.getPlatform().unregisterAll();
}
if (this.audienceProvider != null) {
this.audienceProvider.close();
}
this.fightManager.untagAll();
}
}
@@ -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();
}
}
}
}
@@ -1,50 +0,0 @@
package com.eternalcode.combat.config;
import com.eternalcode.combat.config.composer.DurationComposer;
import net.dzikoysk.cdn.Cdn;
import net.dzikoysk.cdn.CdnFactory;
import java.io.File;
import java.time.Duration;
import java.util.HashSet;
import java.util.Set;
public class ConfigManager {
private final ConfigBackupService backupService;
private final Cdn cdn = CdnFactory
.createYamlLike()
.getSettings()
.withComposer(Duration.class, new DurationComposer())
.build();
private final Set<ReloadableConfig> configs = new HashSet<>();
private final File dataFolder;
public ConfigManager(ConfigBackupService backupService, File dataFolder) {
this.backupService = backupService;
this.dataFolder = dataFolder;
}
public <T extends ReloadableConfig> T load(T config) {
this.backupService.createBackup();
this.cdn.load(config.resource(this.dataFolder), config)
.orThrow(RuntimeException::new);
this.cdn.render(config, config.resource(this.dataFolder))
.orThrow(RuntimeException::new);
this.configs.add(config);
return config;
}
public void reload() {
for (ReloadableConfig config : this.configs) {
this.load(config);
}
}
}
@@ -1,12 +0,0 @@
package com.eternalcode.combat.config;
import net.dzikoysk.cdn.source.Resource;
import java.io.File;
public interface ReloadableConfig {
Resource resource(File folder);
}
@@ -1,24 +0,0 @@
package com.eternalcode.combat.config.composer;
import dev.rollczi.litecommands.shared.EstimatedTemporalAmountParser;
import panda.std.Result;
import java.time.Duration;
public class DurationComposer implements SimpleComposer<Duration> {
@Override
public Result<Duration, Exception> deserialize(String source) {
if (source.isEmpty() || source.equals("0")) {
return Result.ok(Duration.ZERO);
}
return Result.supplyThrowing(IllegalArgumentException.class, () -> EstimatedTemporalAmountParser.TIME_UNITS.parse(source));
}
@Override
public Result<String, Exception> serialize(Duration entity) {
return Result.ok(EstimatedTemporalAmountParser.TIME_UNITS.format(entity));
}
}
@@ -1,9 +0,0 @@
package com.eternalcode.combat.config.composer;
import net.dzikoysk.cdn.serdes.Composer;
import net.dzikoysk.cdn.serdes.SimpleDeserializer;
import net.dzikoysk.cdn.serdes.SimpleSerializer;
public interface SimpleComposer<T> extends Composer<T>, SimpleDeserializer<T>, SimpleSerializer<T> {
}
@@ -1,207 +0,0 @@
package com.eternalcode.combat.config.implementation;
import com.eternalcode.combat.config.ReloadableConfig;
import com.eternalcode.combat.drop.DropSettings;
import com.eternalcode.combat.WhitelistBlacklistMode;
import com.eternalcode.combat.fight.pearl.FightPearlSettings;
import com.eternalcode.combat.notification.NotificationType;
import net.dzikoysk.cdn.entity.Contextual;
import net.dzikoysk.cdn.entity.Description;
import net.dzikoysk.cdn.source.Resource;
import net.dzikoysk.cdn.source.Source;
import org.bukkit.Material;
import org.bukkit.entity.EntityType;
import org.bukkit.event.entity.EntityDamageEvent;
import java.io.File;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
public class PluginConfig implements ReloadableConfig {
@Description("# Do you want to change the plugin settings?")
public Settings settings = new Settings();
@Description({" ", "# Ender pearl settings"})
public FightPearlSettings pearl = new FightPearlSettings();
@Description({ " ", "# Set a custom way for a player's items to drop on death (if in combat)" })
public DropSettings dropSettings = new DropSettings();
@Contextual
public static class Settings {
@Description("# Whether the player should receive information about new plugin updates upon joining the server")
public boolean shouldReceivePluginUpdates = true;
@Description("# The duration of combat in seconds")
public Duration combatDuration = Duration.ofSeconds(20);
@Description("# List of worlds to ignore")
public List<String> worldsToIgnore = List.of(
"your_world"
);
@Description("# List of regions to block")
public List<String> blockedRegions = Collections.singletonList("your_region");
@Description("# Set the knock multiplier for the blocked region")
public double blockedRegionKnockMultiplier = 1.2;
@Description("# Set the radius of the blocked region if you aren't using WorldGuard basen on default spawn region!")
public int blockedRegionRadius = 10;
@Description("# Release attacker after victim dies?")
public boolean shouldReleaseAttacker = true;
@Description("# Combat log notification type, available types: ACTION_BAR, CHAT, TITLE, SUBTITLE")
public NotificationType notificationType = NotificationType.ACTION_BAR;
@Description("# Command blocking mode, available modes: WHITELIST, BLACKLIST")
public WhitelistBlacklistMode commandBlockingMode = WhitelistBlacklistMode.BLACKLIST;
@Description({
"# List of commands based on the mode above",
"# Based on BLACKLIST mode, all commands in the list are blocked, and all others are allowed",
"# Based on WHITELIST mode, all commands in the list are allowed, and all others are blocked",
})
public List<String> blockedCommands = List.of(
"gamemode",
"spawn",
"tp"
);
@Description("# Block the use of elytra?")
public boolean shouldPreventElytraUsage = true;
@Description("# Disable the use of elytra on damage?")
public boolean shouldElytraDisableOnDamage = true;
@Description("# Block the opening of inventory?")
public boolean shouldPreventInventoryOpening = true;
@Description("# Whether to block the placement of blocks?")
public boolean shouldPreventBlockPlacing = true;
@Description("# The minimum Y level at which block placing is blocked")
public int minBlockPlacingLevel = 40;
@Description({
"# Disable placing specific blocks?",
"# If you want to block all blocks, enable shouldPreventBlockPlacing and make this list empty",
"# If you want to disable placing only specific blocks, enable shouldPreventBlockPlacing and add blocks to this list above",
"# If you want to disable this feature completely, disable shouldPreventBlockPlacing option above",
})
public List<Material> specificBlocksToPreventPlacing = List.of();
@Description("# Do You want to enable combat log for non-player causes of damage? - Set to false to disable")
public boolean shouldEnableDamageCauses = false;
@Description("# Select the mode for damage causes, available modes: WHITELIST, BLACKLIST")
public WhitelistBlacklistMode damageCausesMode = WhitelistBlacklistMode.WHITELIST;
@Description({
"# After selecting the mode above, select the causes of damage to be logged",
"# You can find a list of all causes here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityDamageEvent.DamageCause.html",
"# While using Whitelist mode the player will get a combat log after the damage from the list below",
"# While using Blacklist mode the player will get a combat log after any damage non-listed below",
})
public List<EntityDamageEvent.DamageCause> damageCausesToLog = List.of(
EntityDamageEvent.DamageCause.LAVA,
EntityDamageEvent.DamageCause.CONTACT,
EntityDamageEvent.DamageCause.FIRE,
EntityDamageEvent.DamageCause.FIRE_TICK
);
@Description({
"# After what type of projectile entity should not tag the player as fighter?",
"# You can find a list of all entity types here: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html"
})
public List<EntityType> disabledProjectileEntities = List.of(
EntityType.ENDER_PEARL
);
}
@Description({ " ", "# Do you want to change the plugin messages?" })
public Messages messages = new Messages();
@Contextual
public static class Messages {
@Description("# Do you want to change the admin messages?")
public AdminMessages admin = new AdminMessages();
@Description({ " ", "# Combat log message format, e.g. on the actionbar (you can use {TIME} variable to display the time left in combat)" })
public String combatFormat = "&dCombat ends in: &f{TIME}";
@Description("# Message sent when the player does not have permission to perform a command")
public String noPermission = "&cYou don't have permission to perform this command!";
@Description("# Message sent when the specified player could not be found")
public String playerNotFound = "&cThe specified player could not be found!";
@Description("# Message sent when the player enters combat")
public String playerTagged = "&cYou are in combat, do not leave the server!";
@Description("# Message sent when the player leaves combat")
public String playerUntagged = "&aYou are no longer in combat! You can safely leave the server.";
@Description("# This is broadcast when the player is in combat and logs out")
public String playerLoggedOutDuringCombat = "&c{PLAYER} logged off during the fight!";
@Description({
"# Message sent when the player is in combat and tries to use a disabled command",
"# you can configure the list of disabled commands in the blockedCommands section of the config.yml file"
})
public String commandDisabledDuringCombat = "&cUsing this command during combat is prohibited!";
@Description("# Message sent when player tries to use a command with invalid arguments")
public String invalidCommandUsage = "&7Correct usage: &e{USAGE}";
@Description("# Message sent when player tries to open inventory, but the inventory open is blocked")
public String inventoryBlockedDuringCombat = "&cYou cannot open this inventory during combat!";
@Description("# Message sent when player tries to place a block, but the block place is blocked")
public String blockPlacingBlockedDuringCombat = "&cYou cannot place below 40Y coordinate during combat!";
@Description("# Message sent when player tries to enter a region")
public String cantEnterOnRegion = "&cYou can't enter on this region during combat!";
@Contextual
public static class AdminMessages {
@Description("# Message sent when the configuration is reloaded")
public String reload = "&aConfiguration has been successfully reloaded!";
@Description("# Message sent when console tries to use a command that is only for players")
public String onlyForPlayers = "&cThis command is only available to players!";
@Description("# Message sent to admin when they tag a player")
public String adminTagPlayer = "&7You have tagged &e{PLAYER}";
@Description("# Message sent when a player is tagged by an admin")
public String adminTagMultiplePlayers = "&7You have tagged &e{FIRST_PLAYER}&7 and &e{SECOND_PLAYER}&7.";
@Description("# Message sent to admin when they remove a player from combat")
public String adminUntagPlayer = "&7You have removed &e{PLAYER}&7 from the fight.";
@Description("# Message sent when the player is not in combat")
public String adminPlayerNotInCombat = "&cThis player is not in combat!";
@Description("# Message sent when the player is in combat")
public String playerInCombat = "&c{PLAYER} is currently in combat!";
@Description("# Message sent when a player is not in combat")
public String playerNotInCombat = "&a{PLAYER} is not currently in combat.";
@Description("# Message sent when an admin tries to tag themselves")
public String adminCannotTagSelf = "&cYou cannot tag yourself!";
}
}
@Override
public Resource resource(File folder) {
return Source.of(folder, "config.yml");
}
}
@@ -1,52 +0,0 @@
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.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
public class DropController implements Listener {
private final DropManager dropManager;
private final DropSettings dropSettings;
private final FightManager fightManager;
public DropController(DropManager dropManager, DropSettings dropSettings, FightManager fightManager) {
this.dropManager = dropManager;
this.dropSettings = dropSettings;
this.fightManager = fightManager;
}
@EventHandler
void onPlayerDeath(PlayerDeathEvent event) {
Player player = event.getEntity();
DropType dropType = this.dropSettings.dropType;
if (dropType == DropType.UNCHANGED || !this.fightManager.isInCombat(player.getUniqueId())) {
return;
}
FightTag fightTag = this.fightManager.getTag(player.getUniqueId());
Drop drop = Drop.builder()
.player(player)
.killer(player.getKiller())
.fightTag(fightTag)
.deathCause(fightTag.getDeathCause())
.droppedItems(event.getDrops())
.droppedExp(player.getTotalExperience())
.build();
this.dropManager.modify(dropType, drop);
event.getDrops().clear();
event.getDrops().addAll(drop.getDroppedItems());
if (this.dropSettings.affectExperience) {
event.setDroppedExp(drop.getDroppedExp());
}
}
}
@@ -1,24 +0,0 @@
package com.eternalcode.combat.drop;
import net.dzikoysk.cdn.entity.Contextual;
import net.dzikoysk.cdn.entity.Description;
@Contextual
public class DropSettings {
@Description({
"# UNCHANGED - The default way of item drop defined by the engine",
"# PERCENT - Drops a fixed percentage of items",
"# PLAYERS_HEALTH - Drops inverted percentage of the player's health (i.e. if the player has, for example, 80% HP, he will drop 20% of items. Only works when the player escapes from combat by quiting game)"
})
public DropType dropType = DropType.UNCHANGED;
@Description("# What percentage of items should drop from the player? (Only if Drop Type is set to PERCENT)")
public int dropItemPercent = 100;
@Description("# This option is responsible for the lowest percentage of the player that can drop (i.e. if the player leaves the game while he has 100% of his HP, the percentage of items that is set in this option will drop, if you set this option to 0, then nothing will drop from such a player)")
public int playersHealthPercentClamp = 20;
@Description("# Does the drop modification affect the experience drop?")
public boolean affectExperience = false;
}
@@ -1,10 +0,0 @@
package com.eternalcode.combat.fight;
public enum FightDeathCause {
UNKNOWN,
ESCAPE,
KILLED_BY_PLAYER,
KILLED_BY_ENTITY
}
@@ -1,51 +0,0 @@
package com.eternalcode.combat.fight;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.notification.NotificationAnnouncer;
import com.eternalcode.combat.util.DurationUtil;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import panda.utilities.text.Formatter;
import java.time.Duration;
public class FightTask implements Runnable {
private final FightManager fightManager;
private final PluginConfig config;
private final Server server;
private final NotificationAnnouncer announcer;
public FightTask(FightManager fightManager, PluginConfig config, Server server, NotificationAnnouncer announcer) {
this.fightManager = fightManager;
this.config = config;
this.server = server;
this.announcer = announcer;
}
@Override
public void run() {
for (FightTag fightTag : this.fightManager.getFights()) {
Player player = this.server.getPlayer(fightTag.getTaggedPlayer());
if (player == null) {
return;
}
if (!fightTag.isExpired()) {
Duration remaining = fightTag.getRemainingDuration();
Formatter formatter = new Formatter()
.register("{TIME}", DurationUtil.format(remaining));
String format = formatter.format(this.config.messages.combatFormat);
this.announcer.send(player, this.config.settings.notificationType, format);
continue;
}
this.fightManager.untag(fightTag.getTaggedPlayer());
this.announcer.send(player, this.config.settings.notificationType, this.config.messages.playerUntagged);
}
}
}
@@ -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);
}
}
@@ -1,61 +0,0 @@
package com.eternalcode.combat.notification;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.platform.AudienceProvider;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.title.Title;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.Map;
import java.util.function.BiConsumer;
public final class NotificationAnnouncer {
private final static Map<NotificationType, BiConsumer<Audience, Component>> NOTIFICATION_HANDLERS = Map.of(
NotificationType.CHAT, Audience::sendMessage,
NotificationType.ACTION_BAR, Audience::sendActionBar,
NotificationType.TITLE, (audience, component) -> audience.showTitle(Title.title(component, Component.empty())),
NotificationType.SUBTITLE, (audience, component) -> audience.showTitle(Title.title(Component.empty(), component))
);
private final AudienceProvider audienceProvider;
private final MiniMessage miniMessage;
public NotificationAnnouncer(AudienceProvider audienceProvider, MiniMessage miniMessage) {
this.audienceProvider = audienceProvider;
this.miniMessage = miniMessage;
}
public void send(CommandSender commandSender, NotificationType notificationType, String text) {
Audience audience = this.audience(commandSender);
Component component = this.miniMessage.deserialize(text);
BiConsumer<Audience, Component> handler = NOTIFICATION_HANDLERS.get(notificationType);
if (handler == null) {
return;
}
handler.accept(audience, component);
}
public void sendMessage(CommandSender commandSender, String text) {
Audience audience = this.audience(commandSender);
audience.sendMessage(this.miniMessage.deserialize(text));
}
public void broadcast(CommandSender commandSender, String text) {
this.audienceProvider.all().sendMessage(this.miniMessage.deserialize(text));
}
private Audience audience(CommandSender sender) {
if (sender instanceof Player player) {
return this.audienceProvider.player(player.getUniqueId());
}
return this.audienceProvider.console();
}
}
@@ -1,32 +0,0 @@
package com.eternalcode.combat.region;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.regions.RegionContainer;
import com.sk89q.worldguard.protection.regions.RegionQuery;
import org.bukkit.Location;
import java.util.List;
public class WorldGuardRegionProvider implements RegionProvider {
private final List<String> regions;
public WorldGuardRegionProvider(List<String> regions) {
this.regions = regions;
}
@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();
ApplicableRegionSet applicableRegions = regionQuery.getApplicableRegions(BukkitAdapter.adapt(location));
return applicableRegions.getRegions().stream().anyMatch(region -> region.getId().equalsIgnoreCase(regionName));
}
}
Loaded 100 of 106 files, more files were not shown because too many files have changed in this diff. Show more