Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9502ed2fd9
|
||
|
|
c3eb6c5595
|
||
|
|
410aaf1eb7 | ||
|
|
4ab18417be | ||
|
|
d739ed6d1f | ||
|
|
a98f16d0df | ||
|
|
40c3178312 | ||
|
|
70505da275 | ||
|
|
710be6064a | ||
|
|
56d34cc781 | ||
|
|
50116b867c | ||
|
|
4d74dca4f1 | ||
|
|
8ae2ad9db6 | ||
|
|
ea3997a97b | ||
|
|
1153258192 | ||
|
|
1f3b4a7215 | ||
|
|
cc85e0ad0c | ||
|
|
6f6ec3e785 | ||
|
|
1efd1ebe79 | ||
|
|
5abb072ea0 | ||
|
|
8e93f90459 | ||
|
|
459ff06f18 | ||
|
|
4a153d5557 | ||
|
|
3e5b060923 | ||
|
|
03768ccd8d | ||
|
|
ba59d85996 | ||
|
|
1c432cbcb3 | ||
|
|
de6b76526c | ||
|
|
65131df123 | ||
|
|
db9b40d687 | ||
|
|
1324f4b846 | ||
|
|
e2cabb7aad | ||
|
|
7b7b44457c | ||
|
|
89704d9729 | ||
|
|
566022eaea | ||
|
|
ebb26266d4 | ||
|
|
97c0238487 | ||
|
|
f06ee72792 | ||
|
|
5244e09de9 | ||
|
|
a1dea29fc1 | ||
|
|
c57e22fd22 | ||
|
|
7de452ac15 | ||
|
|
7e578d63f7 | ||
|
|
e5f86c53a2 | ||
|
|
fc16e2f8fe | ||
|
|
838bb16e12 | ||
|
|
22260e87d0 | ||
|
|
fe73466cfb | ||
|
|
c299aed434 | ||
|
|
8dbaf9ae98 | ||
|
|
db4ab96377 | ||
|
|
0dca7e7d42 | ||
|
|
bb080b5ce9 | ||
|
|
9f4ae311a7 | ||
|
|
96deb783b7 | ||
|
|
bee80d22aa | ||
|
|
4202e86eaa | ||
|
|
e109b38705 | ||
|
|
9bba5bd418 | ||
|
|
77940ca7b6 | ||
|
|
a4cc6d0e8b | ||
|
|
2028c5eafd | ||
|
|
042c5848df | ||
|
|
c30860070f | ||
|
|
20c3515082 | ||
|
|
07b22a1141 | ||
|
|
2fc9bc580b | ||
|
|
7730956dbc | ||
|
|
48ef3e5d0a | ||
|
|
abd097401f | ||
|
|
1d0d182cc8 | ||
|
|
60b4e6ee9c | ||
|
|
b096cb5d6f | ||
|
|
d596202278 | ||
|
|
b879497840 | ||
|
|
ccad96ae96 | ||
|
|
0e782a0cf5 | ||
|
|
0979d449c6 | ||
|
|
dee65e5f59 | ||
|
|
01d3491d44 | ||
|
|
9896069577 | ||
|
|
f682508172 | ||
|
|
a988abcd17 | ||
|
|
b0fa197e99 | ||
|
|
5300b5a007 | ||
|
|
c1f282832c | ||
|
|
972fc52e69 | ||
|
|
b79ed28edf | ||
|
|
025f49685c | ||
|
|
4d957d2c8a | ||
|
|
d7a04ef0c2 | ||
|
|
0bd9911df3 | ||
|
|
3062378c66 | ||
|
|
0799081813 | ||
|
|
16a2df1a08 | ||
|
|
cd3681f8f6 | ||
|
|
885f514e17 | ||
|
|
5b84881b94 | ||
|
|
e69a42ba0f | ||
|
|
9783d7a025 | ||
|
|
99a93ce249 | ||
|
|
1c2797225a | ||
|
|
1af5710ac9 | ||
|
|
1938ae409d | ||
|
|
88fa996202 | ||
|
|
e3c4f2c26d | ||
|
|
e87060d2b9 | ||
|
|
dfe70a65d7 | ||
|
|
fbade09087 | ||
|
|
660b8b5519 | ||
|
|
13eb6dead2 | ||
|
|
eafb4c9318 | ||
|
|
bfc632d518 | ||
|
|
02d5a2f46d | ||
|
|
9e3f28ee10 | ||
|
|
e5fb0d4b32 | ||
|
|
3325a2f8d3 | ||
|
|
a0bd8494f8 | ||
|
|
08cfe7c0c4 | ||
|
|
ed30d43736 | ||
|
|
b6347517b6 | ||
|
|
3f729beffe | ||
|
|
337eb7d765 | ||
|
|
b02231bc0c | ||
|
|
ee6f5aecd3 | ||
|
|
6eb89607bf | ||
|
|
9f33de088b | ||
|
|
815b63c7bf | ||
|
|
410289a2b8 | ||
|
|
e40a37e2ff | ||
|
|
53c45aa939 | ||
|
|
625ba81281 | ||
|
|
1bae5c547d | ||
|
|
986895d5a4 | ||
|
|
4776714893 | ||
|
|
6154f26977 | ||
|
|
2f010ee978 | ||
|
|
7d5d074811 | ||
|
|
8315e198b3 | ||
|
|
b0cbdc04c8 | ||
|
|
f46f9ca268 | ||
|
|
d789d3b47e | ||
|
|
dd7eaf2de8 | ||
|
|
a985774272 | ||
|
|
fa000b4a3a | ||
|
|
80edd3b425 | ||
|
|
fc4a6c28a3 | ||
|
|
e4eb847b56 | ||
|
|
f5884119d6 | ||
|
|
0cd9d50086 | ||
|
|
7043d7baff | ||
|
|
26d3f5b1ce | ||
|
|
455b66e136 | ||
|
|
881d9b633a | ||
|
|
ac80f1bb0c | ||
|
|
cd36abae37 | ||
|
|
817b907170 | ||
|
|
b9dfb293e4 | ||
|
|
11bfddfc9f | ||
|
|
74d0f487c3 | ||
|
|
f4cc51e9a1 | ||
|
|
e579ca4a22 | ||
|
|
4c13a61752 | ||
|
|
dad09ba196 | ||
|
|
690678c1ea | ||
|
|
40cac29eb0 | ||
|
|
b9c625cdd6 | ||
|
|
861aad1852 | ||
|
|
4c30b7dad6 | ||
|
|
7a152e0624 | ||
|
|
487fc971bc | ||
|
|
ba5fa97e1e | ||
|
|
f2ab289794 | ||
|
|
241898d62a | ||
|
|
9e914d6166 | ||
|
|
f9bc56bf33 | ||
|
|
d6bf4e628c | ||
|
|
b87e64090a | ||
|
|
7ce6d8bd95 | ||
|
|
2dc6cbf40c | ||
|
|
411e78dda0 | ||
|
|
31d41c900f | ||
|
|
a8b48b8804 | ||
|
|
312d8e08e6 | ||
|
|
7f30d79301 | ||
|
|
05095857ca | ||
|
|
d9f98d3aa2 | ||
|
|
bae1b805a7 | ||
|
|
0b119a28c9 | ||
|
|
a235b799e5 | ||
|
|
9b8b0a8513 | ||
|
|
0da9208323 | ||
|
|
64e366ccd3 | ||
|
|
0540e39af2 | ||
|
|
824313c6ec | ||
|
|
4cd83670a9 | ||
|
|
7f130493f7 | ||
|
|
8c5038e3a4 | ||
|
|
bd4fd131c5 | ||
|
|
f338fc4109 | ||
|
|
da555c65e9 | ||
|
|
726f1aa78c | ||
|
|
0539e69236 | ||
|
|
d54bff5516 | ||
|
|
5c099d2de4 | ||
|
|
5042241e94 | ||
|
|
b63e2584ac | ||
|
|
a6984e2694 | ||
|
|
c7f93d9b25 | ||
|
|
c8483e51e9 | ||
|
|
dfc825cd98 | ||
|
|
bd5a6a4351 | ||
|
|
97fd32a7af | ||
|
|
9203309def | ||
|
|
381007a735 | ||
|
|
534e2cf857 | ||
|
|
20a4e4a2f5 | ||
|
|
fe2569918a | ||
|
|
e17effa4cd | ||
|
|
c63df3ab38 | ||
|
|
24fade593b | ||
|
|
4b12968a7b | ||
|
|
9ad0063aa5 | ||
|
|
7f6c6341f1 | ||
|
|
0620f7fb92 | ||
|
|
d18b2f013a | ||
|
|
7b89dfdcfc | ||
|
|
595370f6a4 |
No files matched your search
@@ -4,4 +4,5 @@
|
||||
gradlew text eol=lf
|
||||
*.bat text eol=crlf
|
||||
|
||||
*.jar binary
|
||||
*.jar binary
|
||||
.gitea/CODEOWNERS linguist-detectable=false
|
||||
@@ -0,0 +1,3 @@
|
||||
# Pull requests request review from the eligible staff team(s).
|
||||
# One approval from either an Admin or Developer satisfies branch protection.
|
||||
* @trueog/Admins @trueog/Developers
|
||||
@@ -1,2 +0,0 @@
|
||||
* @EternalCodeTeam/eternalcodeteam-maintainers
|
||||
/.github/CODEOWNERS @EternalCodeTeam/eternalcodeteam-maintainers
|
||||
@@ -1,128 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
https://discord.gg/FQ7jmGBd6c.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
@@ -1,36 +0,0 @@
|
||||
# Contributing to EternalCombat
|
||||
|
||||
We welcome all contributions to the EternalCombat project!
|
||||
|
||||
### How to Contribute
|
||||
|
||||
1. Fork the repository.
|
||||
2. Create a new branch for your feature or bug fix.
|
||||
3. Make your changes, and test them thoroughly.
|
||||
4. Commit your changes and push them to your fork.
|
||||
5. Submit a pull request to the main repository.
|
||||
|
||||
### Code of Conduct
|
||||
|
||||
We expect all contributors to follow our [code of conduct](CODE_OF_CONDUCT.md).
|
||||
|
||||
### Reporting Issues
|
||||
|
||||
If you find an issue with the plugin, please report it in
|
||||
the [Issues tab](https://github.com/EternalCodeTeam/EternalCombat/issues). Please provide as much information as
|
||||
possible, including the version of Minecraft and the plugin you are using, as well as any error messages or logs.
|
||||
|
||||
### Compatibility
|
||||
|
||||
CombatLog has been tested on versions 1.17.1 - 1.19.3, but probably works on most versions of Minecraft. If you find
|
||||
compatibility issues, please report them in the Issues tab. CombatLog uses java 11+, so you need to have it installed on
|
||||
your server.
|
||||
|
||||
### Submitting a Pull Request
|
||||
|
||||
When submitting a pull request, please make sure to follow these guidelines:
|
||||
|
||||
- Make sure that your code adheres to the plugin's existing coding style.
|
||||
- Test your changes thoroughly before submitting the pull request.
|
||||
- If your pull request is related to an existing issue, please reference the issue in your pull request.
|
||||
- Thank you for your contributions to the EternalCombat project!
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a bug report to help us improve EternalCombat <3
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Platform (please complete the following information):**
|
||||
|
||||
- Java Version: [e.g. JDK Temurin 17]
|
||||
- Server software, ex. PaperMC 1.19.3 build #1
|
||||
|
||||
**Additional notes**
|
||||
Add any other context about the problem here.
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for EternalCombat <3
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -1 +0,0 @@
|
||||
**Please describe the changes made by this PR and why they need to be merged:**
|
||||
@@ -1,43 +0,0 @@
|
||||
name: Java CI with Gradle
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Cache Gradle
|
||||
uses: actions/cache@v4.0.2
|
||||
with:
|
||||
path: ~/.gradle/caches
|
||||
key: >-
|
||||
${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*',
|
||||
'**/gradle-wrapper.properties') }}
|
||||
restore-keys: '${{ runner.os }}-gradle-'
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x gradlew
|
||||
- name: Build with Gradle
|
||||
uses: gradle/gradle-build-action@4c39dd82cd5e1ec7c6fa0173bb41b4b6bb3b86ff
|
||||
with:
|
||||
arguments: shadowJar
|
||||
- name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v4.3.3
|
||||
with:
|
||||
name: 'Successfully build EternalCombat'
|
||||
path: eternalcombat-plugin/build/libs/*.jar
|
||||
@@ -1,28 +0,0 @@
|
||||
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 }}
|
||||
@@ -5,4 +5,5 @@
|
||||
.settings
|
||||
bin
|
||||
build
|
||||
buildSrc/.kotlin/
|
||||
run
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"scanSettings": {
|
||||
"baseBranches": []
|
||||
},
|
||||
"checkRunSettings": {
|
||||
"vulnerableCheckRunConclusionLevel": "failure",
|
||||
"displayMode": "diff",
|
||||
"useMendCheckNames": true
|
||||
},
|
||||
"issueSettings": {
|
||||
"minSeverityLevel": "LOW",
|
||||
"issueType": "DEPENDENCY"
|
||||
}
|
||||
}
|
||||
@@ -1,84 +1,31 @@
|
||||
<div align="center">
|
||||
# EternalCombat-OG
|
||||
|
||||

|
||||
A [TrueOG Network](https://true-og.net) fork of [EternalCombat](https://github.com/EternalCodeTeam/EternalCombat)
|
||||
by the EternalCode Team — a combat-tag / combat-logging plugin for Paper.
|
||||
|
||||
[](https://papermc.io)
|
||||
[](https://spigotmc.org)
|
||||
## License & attribution
|
||||
|
||||
[](https://www.patreon.com/eternalcode)
|
||||
[](https://eternalcode.pl/)
|
||||
[](https://discord.gg/FQ7jmGBd6c)
|
||||
EternalCombat-OG is a derivative work of **EternalCombat** by the **EternalCode Team**, distributed
|
||||
under the **Apache License 2.0**. The original copyright and license are retained; see the
|
||||
[`LICENSE`](LICENSE) file. Upstream project: https://github.com/EternalCodeTeam/EternalCombat
|
||||
|
||||
[](https://gradle.org/)
|
||||
[](https://www.java.com/)
|
||||
</div>
|
||||
## Changes from upstream
|
||||
|
||||
### Information
|
||||
This fork diverges from upstream EternalCombat in the following ways:
|
||||
|
||||
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.
|
||||
- Renamed the project and plugin to `EternalCombat-OG`.
|
||||
- Fixed combat knockback flinging players across the map or into the sky near safe zones.
|
||||
- Players in combat can move between the warzone and wilderness, but not into safe areas.
|
||||
- Render the glass combat border on the true safe/unsafe boundary (e.g. between the warzone and spawn or the market) instead of a region's bounding box, so no wall shows between the warzone and wilderness or on the outer face of a large enclosing region.
|
||||
- Mounted players are pushed along with their mount instead of being thrown off.
|
||||
- Closed gaps that let combat-tagged players slip into shops and the market.
|
||||
- Removed chat gradients.
|
||||
- Added a command whitelist for "The Herobrine" minigames.
|
||||
- Dropped Lands support.
|
||||
- Removed the upstream GitHub Actions, plus build and dependency cleanup.
|
||||
|
||||
## Building
|
||||
Build EternalCombat using:
|
||||
## Requirements
|
||||
|
||||
`./gradlew shadowJar`
|
||||
|
||||
### Permissions for EternalCombat
|
||||
|
||||
- `eternalcombat.status` - Allows to check if the player is in combat `/combatlog status <player>`
|
||||
- `eternalcombat.tag` - Allows to create fights between two
|
||||
players `/combatlog tag <first_player> <optional_second_player>`
|
||||
- `eternalcombat.untag` - Allows to remove player from fight `/combatlog untag <player>`
|
||||
- `eternalcombat.reload` - Allows to reload plugin `/combatlog reload`
|
||||
- `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
|
||||
more information on how to contribute and our [code of conduct](./.github/CODE_OF_CONDUCT.md)
|
||||
|
||||
### Reporting Issues
|
||||
|
||||
If you find an issue with the plugin, please report it in
|
||||
the [Issues tab](https://github.com/EternalCodeTeam/EternalCombat/issues). Please provide as much information as
|
||||
possible, including the version of Minecraft and the plugin you are using, as well as any error messages or logs
|
||||
- Paper (Java 17+ toolchain)
|
||||
- [PacketEvents](https://modrinth.com/plugin/packetevents) (required)
|
||||
- WorldGuard (optional, for region-based combat restrictions)
|
||||
|
After Width: | Height: | Size: 5.2 MiB |
|
After Width: | Height: | Size: 2.6 MiB |
|
After Width: | Height: | Size: 752 KiB |
|
After Width: | Height: | Size: 5.4 MiB |
|
After Width: | Height: | Size: 2.7 MiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 98 KiB |
@@ -0,0 +1,48 @@
|
||||
plugins {
|
||||
java
|
||||
eclipse
|
||||
}
|
||||
|
||||
tasks.jar {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
val pluginProject = project(":eternalcombat-plugin")
|
||||
val pluginJar by tasks.registering(Copy::class) {
|
||||
dependsOn(":eternalcombat-plugin:shadowJar")
|
||||
from(pluginProject.provider {
|
||||
pluginProject.layout.buildDirectory.file("libs/EternalCombat-OG-${pluginProject.version}.jar").get().asFile
|
||||
})
|
||||
into(layout.buildDirectory.dir("libs"))
|
||||
}
|
||||
|
||||
tasks.assemble {
|
||||
dependsOn(pluginJar)
|
||||
}
|
||||
|
||||
allprojects {
|
||||
group = "com.eternalcode"
|
||||
version = "2.2.0"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://repo.papermc.io/repository/maven-public/")
|
||||
maven("https://repo.eternalcode.pl/releases")
|
||||
maven("https://storehouse.okaeri.eu/repository/maven-releases/")
|
||||
maven("https://maven.reposilite.com/releases")
|
||||
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
|
||||
maven("https://repo.codemc.org/repository/maven-public/")
|
||||
maven("https://maven.enginehub.org/repo/")
|
||||
maven("https://jitpack.io")
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply(plugin = "java")
|
||||
apply(plugin = "eclipse")
|
||||
java { toolchain { languageVersion.set(JavaLanguageVersion.of(17)) } }
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
options.encoding = "UTF-8"
|
||||
options.release.set(17)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
eclipse
|
||||
}
|
||||
repositories { gradlePluginPortal(); mavenCentral() }
|
||||
kotlin { jvmToolchain(17) }
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@@ -8,10 +11,10 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.23")
|
||||
implementation("com.github.johnrengelman:shadow:8.1.1")
|
||||
implementation("com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:9.4.1")
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.3.20")
|
||||
implementation("net.minecrell:plugin-yml:0.6.0")
|
||||
implementation("xyz.jpenilla:run-task:2.2.4")
|
||||
implementation("xyz.jpenilla:run-task:3.0.2")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -2,33 +2,36 @@ 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 JUNIT_JUPITER_API = "6.0.3"
|
||||
const val JUNIT_JUPITER_PARAMS = "6.0.3"
|
||||
const val JUNIT_JUPITER_ENGINE = "6.0.3"
|
||||
|
||||
const val ETERNALCODE_COMMONS = "1.1.2"
|
||||
// TODO: Multification.
|
||||
const val JETBRAINS_ANNOTATIONS = "26.1.0"
|
||||
|
||||
const val ADVENTURE_PLATFORM_BUKKIT = "4.3.2"
|
||||
const val ADVENTURE_TEXT_MINIMESSAGE = "4.16.0"
|
||||
const val ETERNALCODE_COMMONS = "1.3.4"
|
||||
const val MULTIFICATION = "1.2.4"
|
||||
const val PACKETEVENTS = "2.11.2"
|
||||
|
||||
const val LITE_COMMANDS = "2.8.9"
|
||||
const val OKAERI_CONFIGS_YAML_BUKKIT = "5.0.1"
|
||||
const val OKAERI_CONFIGS_SERDES_COMMONS = "5.0.1"
|
||||
const val OKAERI_CONFIGS_SERDES_BUKKIT = "5.0.1"
|
||||
const val ADVENTURE_PLATFORM_BUKKIT = "4.4.1"
|
||||
const val ADVENTURE_API = "4.26.1"
|
||||
|
||||
const val PANDA_UTILITIES = "0.5.2-alpha"
|
||||
const val GIT_CHECK = "1.0.0"
|
||||
const val APACHE_COMMONS = "2.16.1"
|
||||
const val LITE_COMMANDS = "3.10.9"
|
||||
const val OKAERI_CONFIGS_SERDES_COMMONS = "5.0.13"
|
||||
const val OKAERI_CONFIGS_SERDES_BUKKIT = "5.0.13"
|
||||
|
||||
const val CAFFEINE = "3.1.8"
|
||||
const val XSERIES = "13.6.0"
|
||||
|
||||
const val B_STATS_BUKKIT = "3.0.2"
|
||||
const val WORLD_GUARD_BUKKIT = "7.0.9"
|
||||
const val CAFFEINE = "3.2.3"
|
||||
|
||||
const val WORLD_GUARD_BUKKIT = "7.0.8"
|
||||
|
||||
const val PLACEHOLDER_API = "2.11.7"
|
||||
const val PAPERLIB = "1.0.8"
|
||||
|
||||
const val WORLDEDIT = "3ISh7ADm" //cannot use numeric version bc of duplicated version on modrinth
|
||||
const val WORLDGUARD = "7.0.8"
|
||||
const val LUCKPERMS = "5.5.17"
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,36 +1,16 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
checkstyle
|
||||
}
|
||||
|
||||
group = "com.eternalcode"
|
||||
version = "1.2.2"
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "10.16.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.1.0-jre")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
version = "2.5.0-SNAPSHOT"
|
||||
|
||||
tasks.compileJava {
|
||||
options.compilerArgs = listOf("-Xlint:deprecation", "-parameters")
|
||||
options.encoding = "UTF-8"
|
||||
options.release = 17
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
@@ -4,7 +4,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = "com.eternalcode"
|
||||
version = "1.2.2"
|
||||
version = "2.5.0-SNAPSHOT"
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
|
||||
@@ -3,12 +3,17 @@ plugins {
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://repo.eternalcode.pl/releases")
|
||||
maven("https://repo.eternalcode.pl/snapshots")
|
||||
|
||||
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/") }
|
||||
mavenCentral()
|
||||
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
|
||||
maven("https://repo.papermc.io/repository/maven-public/")
|
||||
maven("https://storehouse.okaeri.eu/repository/maven-public/")
|
||||
maven("https://repo.panda-lang.org/releases")
|
||||
maven("https://maven.enginehub.org/repo/")
|
||||
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
|
||||
maven("https://repo.codemc.io/repository/maven-releases/")
|
||||
maven("https://jitpack.io/")
|
||||
maven(url = "https://s01.oss.sonatype.org/content/repositories/snapshots/")
|
||||
}
|
||||
@@ -1,45 +1,9 @@
|
||||
plugins {
|
||||
`eternalcombat-java`
|
||||
`eternalcombat-repositories`
|
||||
`eternalcombat-publish`
|
||||
`eternalcombat-java-unit-test`
|
||||
}
|
||||
plugins { `java-library` }
|
||||
|
||||
java { toolchain { languageVersion.set(JavaLanguageVersion.of(17)) } }
|
||||
|
||||
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}")
|
||||
|
||||
compileOnly("io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT")
|
||||
compileOnly("org.jetbrains:annotations:26.0.2")
|
||||
}
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
package com.eternalcode.combat;
|
||||
|
||||
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;
|
||||
import dev.rollczi.litecommands.command.execute.Execute;
|
||||
import dev.rollczi.litecommands.command.permission.Permission;
|
||||
import dev.rollczi.litecommands.command.route.Route;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import panda.utilities.text.Formatter;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.UUID;
|
||||
|
||||
@Route(name = "combatlog", aliases = "combat")
|
||||
public class CombatCommand {
|
||||
|
||||
private final FightManager fightManager;
|
||||
private final ConfigService configService;
|
||||
private final NotificationAnnouncer announcer;
|
||||
private final PluginConfig config;
|
||||
|
||||
public CombatCommand(FightManager fightManager, ConfigService configService, NotificationAnnouncer announcer, PluginConfig config) {
|
||||
this.fightManager = fightManager;
|
||||
this.configService = configService;
|
||||
this.announcer = announcer;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Execute(route = "status", required = 1)
|
||||
@Permission("eternalcombat.status")
|
||||
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(sender, this.fightManager.isInCombat(targetUniqueId)
|
||||
? formatter.format(messages.admin.playerInCombat)
|
||||
: formatter.format(messages.admin.playerNotInCombat));
|
||||
}
|
||||
|
||||
@Execute(route = "tag", required = 1)
|
||||
@Permission("eternalcombat.tag")
|
||||
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());
|
||||
|
||||
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(sender, format);
|
||||
}
|
||||
|
||||
@Execute(route = "tag", required = 2)
|
||||
@Permission("eternalcombat.tag")
|
||||
void tagMultiple(CommandSender sender, @Arg Player firstTarget, @Arg Player secondTarget) {
|
||||
Duration combatTime = this.config.settings.combatDuration;
|
||||
PluginConfig.Messages messages = this.config.messages;
|
||||
|
||||
if (sender.equals(firstTarget) || sender.equals(secondTarget)) {
|
||||
this.announcer.sendMessage(sender, messages.admin.adminCannotTagSelf);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (firstTagEvent.isCancelled()) {
|
||||
this.announcer.sendMessage(sender, firstTagEvent.getCancelMessage());
|
||||
}
|
||||
|
||||
if (secondTagEvent.isCancelled()) {
|
||||
this.announcer.sendMessage(sender, firstTagEvent.getCancelMessage());
|
||||
}
|
||||
|
||||
if (firstTagEvent.isCancelled() && secondTagEvent.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.announcer.sendMessage(sender, format);
|
||||
}
|
||||
|
||||
@Async
|
||||
@Execute(route = "reload")
|
||||
@Permission("eternalcombat.reload")
|
||||
void execute(CommandSender player) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
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.drop.DropKeepInventoryService;
|
||||
import com.eternalcode.combat.fight.FightManager;
|
||||
import com.eternalcode.combat.fight.drop.DropService;
|
||||
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.fight.pearl.FightPearlService;
|
||||
import com.eternalcode.combat.region.RegionProvider;
|
||||
import com.eternalcode.combat.fight.tagout.FightTagOutService;
|
||||
|
||||
public interface EternalCombatApi {
|
||||
|
||||
@@ -15,16 +14,14 @@ public interface EternalCombatApi {
|
||||
|
||||
RegionProvider getRegionProvider();
|
||||
|
||||
FightPearlManager getFightPearlManager();
|
||||
FightPearlService getFightPearlService();
|
||||
|
||||
FightTagOutService getFightTagOutService();
|
||||
|
||||
FightEffectService getFightEffectService();
|
||||
|
||||
DropManager getDropManager();
|
||||
DropService getDropService();
|
||||
|
||||
DropKeepInventoryManager getDropKeepInventoryManager();
|
||||
|
||||
PluginConfig getPluginConfig();
|
||||
DropKeepInventoryService getDropKeepInventoryService();
|
||||
|
||||
}
|
||||
@@ -4,6 +4,10 @@ public final class EternalCombatProvider {
|
||||
|
||||
private static EternalCombatApi ETERNAL_COMBAT_API;
|
||||
|
||||
static boolean isInitialized() {
|
||||
return ETERNAL_COMBAT_API != null;
|
||||
}
|
||||
|
||||
public static EternalCombatApi provide() {
|
||||
if (ETERNAL_COMBAT_API == null) {
|
||||
throw new IllegalStateException("EternalCombatApi has not been initialized yet!");
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
package com.eternalcode.combat.bridge;
|
||||
|
||||
import com.eternalcode.combat.config.implementation.PluginConfig;
|
||||
import com.eternalcode.combat.region.DefaultRegionProvider;
|
||||
import com.eternalcode.combat.region.RegionProvider;
|
||||
import com.eternalcode.combat.region.WorldGuardRegionProvider;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class BridgeService {
|
||||
|
||||
private final PluginConfig pluginConfig;
|
||||
private final PluginManager pluginManager;
|
||||
private final Logger logger;
|
||||
|
||||
private RegionProvider regionProvider;
|
||||
|
||||
public BridgeService(PluginConfig pluginConfig, PluginManager pluginManager, Logger logger) {
|
||||
this.pluginConfig = pluginConfig;
|
||||
this.pluginManager = pluginManager;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
this.initialize("WorldGuard",
|
||||
() -> this.regionProvider = new WorldGuardRegionProvider(this.pluginConfig.settings.blockedRegions),
|
||||
() -> {
|
||||
this.regionProvider = new DefaultRegionProvider(this.pluginConfig.settings.blockedRegionRadius);
|
||||
|
||||
this.logger.warning("WorldGuard is not installed, set to default region provider.");
|
||||
});
|
||||
}
|
||||
|
||||
private void initialize(String pluginName, BridgeInitializer initializer, Runnable runnable) {
|
||||
if (this.pluginManager.isPluginEnabled(pluginName)) {
|
||||
initializer.initialize();
|
||||
|
||||
this.logger.info("Successfully initialized " + pluginName + " bridge.");
|
||||
}
|
||||
else {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
public RegionProvider getRegionProvider() {
|
||||
return this.regionProvider;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package com.eternalcode.combat.command;
|
||||
|
||||
|
||||
import com.eternalcode.combat.config.implementation.PluginConfig;
|
||||
import com.eternalcode.combat.notification.NotificationAnnouncer;
|
||||
import dev.rollczi.litecommands.command.LiteInvocation;
|
||||
import dev.rollczi.litecommands.handle.InvalidUsageHandler;
|
||||
import dev.rollczi.litecommands.schematic.Schematic;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import panda.utilities.text.Formatter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class InvalidUsage implements InvalidUsageHandler<CommandSender> {
|
||||
|
||||
private final PluginConfig config;
|
||||
private final NotificationAnnouncer announcer;
|
||||
|
||||
public InvalidUsage(PluginConfig config, NotificationAnnouncer announcer) {
|
||||
this.config = config;
|
||||
this.announcer = announcer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(CommandSender commandSender, LiteInvocation invocation, Schematic scheme) {
|
||||
List<String> schematics = scheme.getSchematics();
|
||||
|
||||
for (String schematic : schematics) {
|
||||
Formatter formatter = new Formatter()
|
||||
.register("{USAGE}", schematic);
|
||||
|
||||
this.announcer.sendMessage(commandSender, formatter.format(this.config.messages.invalidCommandUsage));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.eternalcode.combat.command;
|
||||
|
||||
import com.eternalcode.combat.config.implementation.PluginConfig;
|
||||
import com.eternalcode.combat.notification.NotificationAnnouncer;
|
||||
import dev.rollczi.litecommands.command.LiteInvocation;
|
||||
import dev.rollczi.litecommands.command.permission.RequiredPermissions;
|
||||
import dev.rollczi.litecommands.handle.PermissionHandler;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import panda.utilities.text.Formatter;
|
||||
import panda.utilities.text.Joiner;
|
||||
|
||||
public class PermissionMessage implements PermissionHandler<CommandSender> {
|
||||
|
||||
private final PluginConfig config;
|
||||
private final NotificationAnnouncer announcer;
|
||||
|
||||
public PermissionMessage(PluginConfig config, NotificationAnnouncer announcer) {
|
||||
this.config = config;
|
||||
this.announcer = announcer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(CommandSender commandSender, LiteInvocation invocation, RequiredPermissions requiredPermissions) {
|
||||
String value = Joiner.on(", ")
|
||||
.join(requiredPermissions.getPermissions())
|
||||
.toString();
|
||||
|
||||
Formatter formatter = new Formatter()
|
||||
.register("{PERMISSION}", value);
|
||||
|
||||
this.announcer.sendMessage(commandSender, formatter.format(this.config.messages.noPermission));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,230 +0,0 @@
|
||||
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,79 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
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,20 +0,0 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,69 +4,31 @@ 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;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class FightManager {
|
||||
public interface FightManager {
|
||||
|
||||
private final Map<UUID, FightTag> fights = new ConcurrentHashMap<>();
|
||||
private final EventCaller eventCaller;
|
||||
boolean isInCombat(UUID player);
|
||||
|
||||
public FightManager(EventCaller eventCaller) {
|
||||
this.eventCaller = eventCaller;
|
||||
}
|
||||
FightTag getTag(UUID target);
|
||||
|
||||
public boolean isInCombat(UUID player) {
|
||||
if (!this.fights.containsKey(player)) {
|
||||
return false;
|
||||
}
|
||||
Collection<FightTag> getFights();
|
||||
|
||||
FightTag fightTag = this.fights.get(player);
|
||||
// Players who tagged the given player during their current combat session.
|
||||
@ApiStatus.Experimental
|
||||
Set<UUID> getOpponents(UUID player);
|
||||
|
||||
return !fightTag.isExpired();
|
||||
}
|
||||
@ApiStatus.Experimental
|
||||
FightTagEvent tag(UUID target, Duration delay, CauseOfTag causeOfTag, @Nullable UUID tagger);
|
||||
|
||||
public FightUntagEvent untag(UUID player, CauseOfUnTag causeOfUnTag) {
|
||||
FightUntagEvent event = this.eventCaller.publishEvent(new FightUntagEvent(player, causeOfUnTag));
|
||||
if (event.isCancelled()) {
|
||||
return event;
|
||||
}
|
||||
FightTagEvent tag(UUID target, Duration delay, CauseOfTag causeOfTag);
|
||||
|
||||
this.fights.remove(player);
|
||||
return event;
|
||||
}
|
||||
FightUntagEvent untag(UUID player, CauseOfUnTag causeOfUnTag);
|
||||
|
||||
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() {
|
||||
return Collections.unmodifiableCollection(this.fights.values());
|
||||
}
|
||||
|
||||
public FightTag getTag(UUID target) {
|
||||
return this.fights.get(target);
|
||||
}
|
||||
|
||||
public void untagAll() {
|
||||
this.fights.clear();
|
||||
}
|
||||
void untagAll();
|
||||
}
|
||||
@@ -3,38 +3,20 @@ package com.eternalcode.combat.fight;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class FightTag {
|
||||
public interface FightTag {
|
||||
|
||||
private final UUID taggedPlayer;
|
||||
private final Instant endOfCombatLog;
|
||||
@Nullable
|
||||
@ApiStatus.Experimental
|
||||
UUID getTagger();
|
||||
|
||||
FightTag(UUID personToAddCombat, Instant endOfCombatLog) {
|
||||
this.taggedPlayer = personToAddCombat;
|
||||
this.endOfCombatLog = endOfCombatLog;
|
||||
Duration getRemainingDuration();
|
||||
|
||||
}
|
||||
boolean isExpired();
|
||||
|
||||
public UUID getTaggedPlayer() {
|
||||
return this.taggedPlayer;
|
||||
}
|
||||
|
||||
public Instant getEndOfCombatLog() {
|
||||
return this.endOfCombatLog;
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return Instant.now().isAfter(this.endOfCombatLog);
|
||||
}
|
||||
|
||||
public Duration getRemainingDuration() {
|
||||
Duration between = Duration.between(Instant.now(), this.endOfCombatLog);
|
||||
|
||||
if (between.isNegative()) {
|
||||
return Duration.ZERO;
|
||||
}
|
||||
|
||||
return between;
|
||||
}
|
||||
Instant getEndOfCombatLog();
|
||||
|
||||
UUID getTaggedPlayer();
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
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) {
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
package com.eternalcode.combat.fight.controller;
|
||||
|
||||
import com.eternalcode.combat.config.implementation.PluginConfig;
|
||||
import com.eternalcode.combat.WhitelistBlacklistMode;
|
||||
import com.eternalcode.combat.fight.FightManager;
|
||||
import com.eternalcode.combat.notification.NotificationAnnouncer;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
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;
|
||||
|
||||
public class FightActionBlockerController implements Listener {
|
||||
|
||||
private final FightManager fightManager;
|
||||
private final NotificationAnnouncer announcer;
|
||||
private final PluginConfig config;
|
||||
|
||||
public FightActionBlockerController(FightManager fightManager, NotificationAnnouncer announcer, PluginConfig config) {
|
||||
this.fightManager = fightManager;
|
||||
this.announcer = announcer;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
void onPlace(BlockPlaceEvent event) {
|
||||
if (!this.config.settings.shouldPreventBlockPlacing) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = event.getPlayer();
|
||||
UUID uniqueId = player.getUniqueId();
|
||||
|
||||
if (!this.fightManager.isInCombat(uniqueId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Block block = event.getBlock();
|
||||
int level = block.getY();
|
||||
|
||||
List<Material> specificBlocksToPreventPlacing = this.config.settings.specificBlocksToPreventPlacing;
|
||||
|
||||
boolean isBlockPlaceLevel = level > this.config.settings.minBlockPlacingLevel;
|
||||
if (isBlockPlaceLevel && specificBlocksToPreventPlacing.isEmpty()) {
|
||||
event.setCancelled(true);
|
||||
this.announcer.sendMessage(player, this.config.messages.blockPlacingBlockedDuringCombat);
|
||||
}
|
||||
|
||||
Material blockMaterial = block.getType();
|
||||
boolean isBlockInDisabledList = specificBlocksToPreventPlacing.contains(blockMaterial);
|
||||
if (isBlockPlaceLevel && isBlockInDisabledList) {
|
||||
event.setCancelled(true);
|
||||
this.announcer.sendMessage(player, this.config.messages.blockPlacingBlockedDuringCombat);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
void onToggleGlide(EntityToggleGlideEvent event) {
|
||||
if (!this.config.settings.shouldPreventElytraUsage) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(event.getEntity() instanceof Player player)) {
|
||||
return;
|
||||
}
|
||||
UUID uniqueId = player.getUniqueId();
|
||||
|
||||
if (!this.fightManager.isInCombat(uniqueId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.isGliding()) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
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;
|
||||
}
|
||||
|
||||
if (!(event.getEntity() instanceof Player player)) {
|
||||
return;
|
||||
}
|
||||
UUID uniqueId = player.getUniqueId();
|
||||
|
||||
if (this.fightManager.isInCombat(uniqueId) && player.isGliding()) {
|
||||
player.setGliding(false);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
void onOpenInventory(InventoryOpenEvent event) {
|
||||
if (!this.config.settings.shouldPreventInventoryOpening) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = (Player) event.getPlayer();
|
||||
UUID uniqueId = player.getUniqueId();
|
||||
|
||||
if (!this.fightManager.isInCombat(uniqueId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
this.announcer.sendMessage(player, this.config.messages.inventoryBlockedDuringCombat);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
UUID playerUniqueId = player.getUniqueId();
|
||||
|
||||
if (!this.fightManager.isInCombat(playerUniqueId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String command = event.getMessage().split(" ")[0].substring(1).toLowerCase();
|
||||
|
||||
boolean isMatchCommand = this.config.settings.blockedCommands.stream()
|
||||
.anyMatch(command::startsWith);
|
||||
|
||||
WhitelistBlacklistMode mode = this.config.settings.commandBlockingMode;
|
||||
|
||||
boolean shouldCancel = mode.shouldBlock(isMatchCommand);
|
||||
|
||||
if (shouldCancel) {
|
||||
event.setCancelled(true);
|
||||
this.announcer.sendMessage(player, this.config.messages.commandDisabledDuringCombat);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package com.eternalcode.combat.fight.controller;
|
||||
|
||||
import com.eternalcode.combat.fight.event.CauseOfUnTag;
|
||||
import com.eternalcode.combat.config.implementation.PluginConfig;
|
||||
import com.eternalcode.combat.fight.FightManager;
|
||||
import com.eternalcode.combat.fight.logout.LogoutService;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
|
||||
public class FightUnTagController implements Listener {
|
||||
|
||||
private final FightManager fightManager;
|
||||
private final PluginConfig config;
|
||||
private final LogoutService logoutService;
|
||||
|
||||
public FightUnTagController(FightManager fightManager, PluginConfig config, LogoutService logoutService) {
|
||||
this.fightManager = fightManager;
|
||||
this.config = config;
|
||||
this.logoutService = logoutService;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
void onPlayerDeath(PlayerDeathEvent event) {
|
||||
Player player = event.getEntity();
|
||||
Player killer = player.getKiller();
|
||||
|
||||
if (!this.fightManager.isInCombat(player.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
CauseOfUnTag cause = this.getDeathCause(player, killer);
|
||||
|
||||
this.fightManager.untag(player.getUniqueId(), cause);
|
||||
|
||||
if (killer != null && this.config.settings.shouldReleaseAttacker) {
|
||||
this.fightManager.untag(killer.getUniqueId(), CauseOfUnTag.ATTACKER_RELEASE);
|
||||
}
|
||||
}
|
||||
|
||||
private CauseOfUnTag getDeathCause(Player player, Player killer) {
|
||||
if (this.logoutService.hasLoggedOut(player.getUniqueId())) {
|
||||
return CauseOfUnTag.LOGOUT;
|
||||
}
|
||||
|
||||
if (killer == null) {
|
||||
return CauseOfUnTag.DEATH;
|
||||
}
|
||||
|
||||
if (this.fightManager.isInCombat(killer.getUniqueId())) {
|
||||
return CauseOfUnTag.DEATH_BY_PLAYER;
|
||||
}
|
||||
|
||||
return CauseOfUnTag.DEATH;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.eternalcode.combat.drop;
|
||||
package com.eternalcode.combat.fight.drop;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.eternalcode.combat.fight.drop;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public interface DropKeepInventoryService {
|
||||
|
||||
List<ItemStack> nextItems(UUID uuid);
|
||||
|
||||
boolean hasItems(UUID uuid);
|
||||
|
||||
void addItems(UUID uuid, List<ItemStack> item);
|
||||
|
||||
void addItem(UUID uuid, ItemStack item);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.eternalcode.combat.drop;
|
||||
package com.eternalcode.combat.fight.drop;
|
||||
|
||||
public interface DropModifier {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.eternalcode.combat.drop;
|
||||
package com.eternalcode.combat.fight.drop;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.eternalcode.combat.fight.drop;
|
||||
|
||||
public interface DropService {
|
||||
|
||||
DropResult modify(DropType dropType, Drop drop);
|
||||
|
||||
void registerModifier(DropModifier dropModifier);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.eternalcode.combat.drop;
|
||||
package com.eternalcode.combat.fight.drop;
|
||||
|
||||
public enum DropType {
|
||||
|
||||
@@ -1,75 +1,25 @@
|
||||
package com.eternalcode.combat.fight.effect;
|
||||
|
||||
import java.util.List;
|
||||
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;
|
||||
/**
|
||||
* Manages custom potion effects on players during combat.
|
||||
* Active effects before combat are stored and restored after combat ends.
|
||||
*/
|
||||
public interface FightEffectService {
|
||||
|
||||
public class FightEffectService {
|
||||
void removeCustomEffect(Player player, PotionEffectType type, Integer amplifier);
|
||||
|
||||
private final Map<UUID, List<PotionEffect>> activeEffects = new HashMap<>();
|
||||
private static final int INFINITE_DURATION = -1;
|
||||
void applyCustomEffect(Player player, PotionEffectType type, Integer amplifier);
|
||||
|
||||
public void storeActiveEffect(Player player, PotionEffect effect) {
|
||||
List<PotionEffect> effects = this.activeEffects.computeIfAbsent(player.getUniqueId(), k -> new ArrayList<>());
|
||||
List<PotionEffect> getCurrentEffects(Player player);
|
||||
|
||||
effects.add(effect);
|
||||
}
|
||||
void clearStoredEffects(Player player);
|
||||
|
||||
public void restoreActiveEffects(Player player) {
|
||||
List<PotionEffect> currentEffects = this.getCurrentEffects(player);
|
||||
void restoreActiveEffects(Player 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);
|
||||
}
|
||||
void storeActiveEffect(Player player, PotionEffect effect);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.eternalcode.combat.fight.event;
|
||||
|
||||
public enum CancelTagReason {
|
||||
|
||||
TAGOUT,
|
||||
PERMISSION_BYPASS,
|
||||
ADMIN,
|
||||
CREATIVE_MODE,
|
||||
|
||||
}
|
||||
@@ -1,8 +1,30 @@
|
||||
package com.eternalcode.combat.fight.event;
|
||||
|
||||
public enum CauseOfTag {
|
||||
|
||||
/**
|
||||
* The player was tagged in combat as a result of an attack by another player.
|
||||
*/
|
||||
PLAYER,
|
||||
|
||||
/**
|
||||
* The player was tagged in combat due to an interaction with a non-player entity
|
||||
* (e.g., mobs or environmental damage).
|
||||
*/
|
||||
NON_PLAYER,
|
||||
|
||||
/**
|
||||
* A command was executed to apply a combat tag to the player.
|
||||
*/
|
||||
COMMAND,
|
||||
|
||||
/**
|
||||
* Crystal or anchor explosion caused the tag.
|
||||
*/
|
||||
CRYSTAL,
|
||||
|
||||
/**
|
||||
* A custom cause, typically defined by external plugins or systems, applied the combat tag.
|
||||
*/
|
||||
CUSTOM
|
||||
}
|
||||
@@ -1,11 +1,43 @@
|
||||
package com.eternalcode.combat.fight.event;
|
||||
|
||||
public enum CauseOfUnTag {
|
||||
/**
|
||||
* The combat tag expired after a set duration.
|
||||
*/
|
||||
TIME_EXPIRED,
|
||||
|
||||
/**
|
||||
* The player died, which resulted in the removal of the combat tag.
|
||||
*/
|
||||
DEATH,
|
||||
|
||||
/**
|
||||
* The player was killed by another player, causing the combat tag to be removed.
|
||||
*/
|
||||
DEATH_BY_PLAYER,
|
||||
|
||||
/**
|
||||
* The player logged out, triggering the removal of the combat tag.
|
||||
*/
|
||||
LOGOUT,
|
||||
|
||||
/**
|
||||
* A command was executed to remove the player's combat tag.
|
||||
*/
|
||||
COMMAND,
|
||||
|
||||
/**
|
||||
* The attacker released the player, ending the combat and removing the tag.
|
||||
*/
|
||||
ATTACKER_RELEASE,
|
||||
|
||||
/**
|
||||
* All of the player's combat opponents logged out, so there was nobody left to fight.
|
||||
*/
|
||||
OPPONENT_LOGOUT,
|
||||
|
||||
/**
|
||||
* A custom cause, typically defined by external plugins or systems.
|
||||
*/
|
||||
CUSTOM
|
||||
}
|
||||
@@ -5,59 +5,80 @@ import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This event is triggered when a player is tagged in a fight.
|
||||
*/
|
||||
public class FightTagEvent extends Event implements Cancellable {
|
||||
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
private static final HandlerList HANDLER_LIST = new HandlerList();
|
||||
private final UUID player;
|
||||
private final CauseOfTag cause;
|
||||
private boolean isCancelled = false;
|
||||
private String cancelMessage = "";
|
||||
private boolean cancelled = false;
|
||||
private CancelTagReason cancelReason;
|
||||
|
||||
public FightTagEvent(UUID player, CauseOfTag cause) {
|
||||
super(false);
|
||||
|
||||
this.player = player;
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UUID of the player who was tagged.
|
||||
*
|
||||
* @return The UUID of the tagged player.
|
||||
*/
|
||||
public UUID getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the reason why the player was tagged.
|
||||
*
|
||||
* @return The cause of the combat tag as a {@link CauseOfTag}.
|
||||
*/
|
||||
public CauseOfTag getCause() {
|
||||
return this.cause;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.isCancelled;
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
throw new UnsupportedOperationException("Use #cancel(String) instead");
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
public void cancel(String cancelMessage) {
|
||||
this.isCancelled = true;
|
||||
this.cancelMessage = cancelMessage;
|
||||
/**
|
||||
* Gets the reason why the event was cancelled.
|
||||
*
|
||||
* @return The cancel reason.
|
||||
*/
|
||||
public CancelTagReason getCancelReason() {
|
||||
return this.cancelReason;
|
||||
}
|
||||
|
||||
public void allow() {
|
||||
this.isCancelled = false;
|
||||
this.cancelMessage = "";
|
||||
}
|
||||
|
||||
public String getCancelMessage() {
|
||||
return this.cancelMessage;
|
||||
/**
|
||||
* Sets the reason why the event was cancelled.
|
||||
*
|
||||
* @param cancelReason The cancel reason.
|
||||
*/
|
||||
public void setCancelReason(CancelTagReason cancelReason) {
|
||||
this.cancelReason = cancelReason;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,43 +5,61 @@ import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This event is triggered when a player is untagged during a fight.
|
||||
*/
|
||||
public class FightUntagEvent extends Event implements Cancellable {
|
||||
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
private static final HandlerList HANDLER_LIST = new HandlerList();
|
||||
|
||||
private final UUID player;
|
||||
private final CauseOfUnTag cause;
|
||||
private boolean isCancelled = false;
|
||||
private boolean cancelled;
|
||||
|
||||
public FightUntagEvent(UUID player, CauseOfUnTag cause) {
|
||||
super(false);
|
||||
|
||||
this.player = player;
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UUID of the player who was untagged.
|
||||
*
|
||||
* @return The UUID of the untagged player.
|
||||
*/
|
||||
public UUID getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the reason for why the player was untagged.
|
||||
*
|
||||
* @return The cause of untagging as a {@link CauseOfUnTag}.
|
||||
*/
|
||||
public CauseOfUnTag getCause() {
|
||||
return this.cause;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.isCancelled;
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.isCancelled = cancelled;
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package com.eternalcode.combat.fight.logout;
|
||||
|
||||
import com.eternalcode.combat.config.implementation.PluginConfig;
|
||||
import com.eternalcode.combat.fight.FightManager;
|
||||
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 LogoutController implements Listener {
|
||||
|
||||
private final FightManager fightManager;
|
||||
private final LogoutService logoutService;
|
||||
private final NotificationAnnouncer announcer;
|
||||
private final PluginConfig config;
|
||||
|
||||
|
||||
public LogoutController(FightManager fightManager, LogoutService logoutService, NotificationAnnouncer announcer, PluginConfig config) {
|
||||
this.fightManager = fightManager;
|
||||
this.logoutService = logoutService;
|
||||
this.announcer = announcer;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@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());
|
||||
|
||||
String format = formatter.format(this.config.messages.playerLoggedOutDuringCombat);
|
||||
this.announcer.broadcast(format);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package com.eternalcode.combat.fight.pearl;
|
||||
|
||||
import com.eternalcode.combat.fight.FightManager;
|
||||
import com.eternalcode.combat.notification.NotificationAnnouncer;
|
||||
import com.eternalcode.combat.util.DurationUtil;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.EnderPearl;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import panda.utilities.text.Formatter;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FightPearlController implements Listener {
|
||||
|
||||
private final FightPearlSettings settings;
|
||||
private final NotificationAnnouncer announcer;
|
||||
private final FightManager fightManager;
|
||||
private final FightPearlManager fightPearlManager;
|
||||
|
||||
public FightPearlController(FightPearlSettings settings, NotificationAnnouncer announcer, FightManager fightManager, FightPearlManager fightPearlManager) {
|
||||
this.settings = settings;
|
||||
this.announcer = announcer;
|
||||
this.fightManager = fightManager;
|
||||
this.fightPearlManager = fightPearlManager;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
void onInteract(PlayerInteractEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
UUID uniqueId = player.getUniqueId();
|
||||
|
||||
if (!this.settings.pearlThrowBlocked) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.fightManager.isInCombat(uniqueId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack item = event.getItem();
|
||||
if (item == null || item.getType() != Material.ENDER_PEARL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Action action = event.getAction();
|
||||
if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.settings.pearlThrowDelay.isZero()) {
|
||||
event.setCancelled(true);
|
||||
this.announcer.sendMessage(player, this.settings.pearlThrowBlockedDuringCombat);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.fightPearlManager.hasDelay(uniqueId)) {
|
||||
event.setCancelled(true);
|
||||
|
||||
Duration remainingPearlDelay = this.fightPearlManager.getRemainingDelay(uniqueId);
|
||||
|
||||
Formatter formatter = new Formatter()
|
||||
.register("{TIME}", DurationUtil.format(remainingPearlDelay));
|
||||
|
||||
String format = formatter.format(this.settings.pearlThrowBlockedDelayDuringCombat);
|
||||
this.announcer.sendMessage(player, format);
|
||||
return;
|
||||
}
|
||||
|
||||
this.fightPearlManager.markDelay(uniqueId);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
void onEntityDamage(EntityDamageByEntityEvent event) {
|
||||
if (this.settings.pearlThrowDamageEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(event.getEntity() instanceof Player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(event.getDamager() instanceof EnderPearl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getCause() != EntityDamageEvent.DamageCause.FALL) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setDamage(0.0D);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.eternalcode.combat.fight.pearl;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FightPearlManager {
|
||||
|
||||
private final FightPearlSettings pearlSettings;
|
||||
private final Cache<UUID, Instant> pearlDelays;
|
||||
|
||||
public FightPearlManager(FightPearlSettings pearlSettings) {
|
||||
this.pearlSettings = pearlSettings;
|
||||
this.pearlDelays = Caffeine.newBuilder()
|
||||
.expireAfterWrite(pearlSettings.pearlThrowDelay)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void markDelay(UUID uuid) {
|
||||
this.pearlDelays.put(uuid, Instant.now().plus(this.pearlSettings.pearlThrowDelay));
|
||||
}
|
||||
|
||||
public boolean hasDelay(UUID uuid) {
|
||||
return Instant.now().isBefore(this.getDelay(uuid));
|
||||
}
|
||||
|
||||
public Duration getRemainingDelay(UUID uuid) {
|
||||
return Duration.between(Instant.now(), this.getDelay(uuid));
|
||||
}
|
||||
|
||||
public Instant getDelay(UUID uuid) {
|
||||
return this.pearlDelays.asMap().getOrDefault(uuid, Instant.MIN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.eternalcode.combat.fight.pearl;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface FightPearlService {
|
||||
|
||||
Instant getDelay(UUID uuid);
|
||||
|
||||
Duration getRemainingDelay(UUID uuid);
|
||||
|
||||
boolean hasDelay(UUID uuid);
|
||||
|
||||
void markDelay(UUID uuid);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package com.eternalcode.combat.fight.pearl;
|
||||
|
||||
import eu.okaeri.configs.OkaeriConfig;
|
||||
import eu.okaeri.configs.annotation.Comment;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public class FightPearlSettings extends OkaeriConfig {
|
||||
|
||||
@Comment({ "# Is pearl damage to be enabled?", "# This will work globally" })
|
||||
public boolean pearlThrowDamageEnabled = true;
|
||||
|
||||
@Comment("# Set true, If you want to lock pearls during the combat")
|
||||
public boolean pearlThrowBlocked = false;
|
||||
|
||||
@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);
|
||||
|
||||
@Comment("# Message sent when player tries to throw ender pearl, but are disabled")
|
||||
public String pearlThrowBlockedDuringCombat = "&cThrowing ender pearls is prohibited during combat!";
|
||||
|
||||
@Comment("# Message sent when player tries to throw ender pearl, but has delay")
|
||||
public String pearlThrowBlockedDelayDuringCombat = "&cYou must wait {TIME} before next throw!";
|
||||
|
||||
}
|
||||
@@ -1,34 +1,13 @@
|
||||
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 {
|
||||
public interface FightTagOutService {
|
||||
|
||||
private final Map<UUID, Instant> tagOuts = new HashMap<>();
|
||||
boolean isTaggedOut(UUID player);
|
||||
|
||||
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);
|
||||
}
|
||||
void unTagOut(UUID player);
|
||||
|
||||
void tagOut(UUID player, Duration duration);
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.eternalcode.combat.notification;
|
||||
|
||||
public interface Notification {
|
||||
|
||||
String message();
|
||||
|
||||
NotificationType type();
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
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 +0,0 @@
|
||||
package com.eternalcode.combat.notification;
|
||||
|
||||
public enum NotificationType {
|
||||
CHAT,
|
||||
ACTION_BAR,
|
||||
TITLE,
|
||||
SUB_TITLE,
|
||||
BOSS_BAR
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package com.eternalcode.combat.region;
|
||||
|
||||
import org.bukkit.Location;
|
||||
|
||||
public class DefaultRegionProvider implements RegionProvider {
|
||||
|
||||
private final int radius;
|
||||
|
||||
public DefaultRegionProvider(int radius) {
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInRegion(Location location) {
|
||||
Location spawnLocation = location.getWorld().getSpawnLocation();
|
||||
double x = spawnLocation.getX();
|
||||
double z = spawnLocation.getZ();
|
||||
|
||||
Point min = new Point(x - this.radius, z - this.radius);
|
||||
Point max = new Point(x + this.radius, z + this.radius);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public record Point(double x, double z) {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.eternalcode.combat.region;
|
||||
|
||||
import org.bukkit.World;
|
||||
|
||||
public record Point(World world, double x, double z) {
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.eternalcode.combat.region;
|
||||
|
||||
import org.bukkit.Location;
|
||||
|
||||
public interface Region {
|
||||
|
||||
Point getCenter();
|
||||
|
||||
Location getMin();
|
||||
|
||||
Location getMax();
|
||||
|
||||
default boolean contains(Location location) {
|
||||
return this.contains(location.getX(), location.getY(), location.getZ());
|
||||
}
|
||||
|
||||
default boolean contains(double x, double y, double z) {
|
||||
Location min = this.getMin();
|
||||
Location max = this.getMax();
|
||||
return x >= min.getX() && x < max.getX()
|
||||
&& y >= min.getY() && y < max.getY()
|
||||
&& z >= min.getZ() && z < max.getZ();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package com.eternalcode.combat.region;
|
||||
|
||||
import com.eternalcode.combat.config.implementation.PluginConfig;
|
||||
import com.eternalcode.combat.fight.FightManager;
|
||||
import com.eternalcode.combat.notification.NotificationAnnouncer;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class RegionController implements Listener {
|
||||
|
||||
private final NotificationAnnouncer announcer;
|
||||
private final RegionProvider regionProvider;
|
||||
private final FightManager fightManager;
|
||||
private final PluginConfig pluginConfig;
|
||||
|
||||
public RegionController(NotificationAnnouncer announcer, RegionProvider regionProvider, FightManager fightManager, PluginConfig pluginConfig) {
|
||||
this.announcer = announcer;
|
||||
this.regionProvider = regionProvider;
|
||||
this.fightManager = fightManager;
|
||||
this.pluginConfig = pluginConfig;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
void onPlayerMove(PlayerMoveEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
if (!this.fightManager.isInCombat(player.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Location locationTo = event.getTo();
|
||||
int xTo = locationTo.getBlockX();
|
||||
int yTo = locationTo.getBlockY();
|
||||
int zTo = locationTo.getBlockZ();
|
||||
|
||||
Location locationFrom = event.getFrom();
|
||||
int xFrom = locationFrom.getBlockX();
|
||||
int yFrom = locationFrom.getBlockY();
|
||||
int zFrom = locationFrom.getBlockZ();
|
||||
|
||||
if (xTo != xFrom || yTo != yFrom || zTo != zFrom) {
|
||||
if (!this.regionProvider.isInRegion(locationTo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
player.setVelocity(vector);
|
||||
|
||||
this.announcer.sendMessage(player, this.pluginConfig.messages.cantEnterOnRegion);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerTeleport(PlayerTeleportEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
if (!this.fightManager.isInCombat(player.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Location targetLocation = event.getTo();
|
||||
|
||||
if (this.regionProvider.isInRegion(targetLocation)) {
|
||||
event.setCancelled(true);
|
||||
this.announcer.sendMessage(player, this.pluginConfig.messages.cantEnterOnRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,18 @@
|
||||
package com.eternalcode.combat.region;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
|
||||
public interface RegionProvider {
|
||||
|
||||
boolean isInRegion(Location location);
|
||||
Optional<Region> getRegion(Location location);
|
||||
|
||||
Location getRegionCenter(Location location);
|
||||
default boolean isInRegion(Location location) {
|
||||
return this.getRegion(location).isPresent();
|
||||
}
|
||||
|
||||
Collection<Region> getRegions(World world);
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package com.eternalcode.combat.updater;
|
||||
|
||||
import com.eternalcode.gitcheck.GitCheck;
|
||||
import com.eternalcode.gitcheck.GitCheckResult;
|
||||
import com.eternalcode.gitcheck.git.GitRepository;
|
||||
import com.eternalcode.gitcheck.git.GitTag;
|
||||
import org.bukkit.plugin.PluginDescriptionFile;
|
||||
import panda.std.Lazy;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class UpdaterService {
|
||||
|
||||
private static final GitRepository GIT_REPOSITORY = GitRepository.of("EternalCodeTeam", "EternalCombat");
|
||||
|
||||
private final GitCheck gitCheck = new GitCheck();
|
||||
private final Lazy<GitCheckResult> gitCheckResult;
|
||||
|
||||
public UpdaterService(PluginDescriptionFile descriptionFile) {
|
||||
this.gitCheckResult = new Lazy<>(() -> {
|
||||
String version = descriptionFile.getVersion();
|
||||
|
||||
return this.gitCheck.checkRelease(GIT_REPOSITORY, GitTag.of("v" + version));
|
||||
});
|
||||
}
|
||||
|
||||
public CompletableFuture<Boolean> isUpToDate() {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
GitCheckResult result = this.gitCheckResult.get();
|
||||
|
||||
return result.isUpToDate();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +1,215 @@
|
||||
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription
|
||||
import io.papermc.hangarpublishplugin.model.Platforms
|
||||
plugins {
|
||||
`eternalcombat-java`
|
||||
`eternalcombat-repositories`
|
||||
|
||||
id("net.minecrell.plugin-yml.bukkit")
|
||||
id("com.github.johnrengelman.shadow")
|
||||
id("com.gradleup.shadow")
|
||||
id("xyz.jpenilla.run-paper")
|
||||
id("com.modrinth.minotaur") version "2.9.0"
|
||||
id("io.papermc.hangar-publish-plugin") version "0.1.4"
|
||||
}
|
||||
|
||||
val buildNumber = providers.environmentVariable("GITHUB_RUN_NUMBER").orNull
|
||||
val isRelease = providers.environmentVariable("GITHUB_EVENT_NAME").orNull == "release"
|
||||
|
||||
if (buildNumber != null && !isRelease) {
|
||||
val offset = try {
|
||||
val description = providers.exec {
|
||||
commandLine("git", "describe", "--tags", "--long")
|
||||
}.standardOutput.asText.get().trim()
|
||||
val parts = description.split("-")
|
||||
if (parts.size >= 3) parts[parts.size - 2] else "0"
|
||||
} catch (e: Exception) {
|
||||
try {
|
||||
providers.exec {
|
||||
commandLine("git", "rev-list", "--count", "HEAD")
|
||||
}.standardOutput.asText.get().trim()
|
||||
} catch (e: Exception) {
|
||||
"0"
|
||||
}
|
||||
}
|
||||
|
||||
val baseVersion = project.version.toString().replace("-SNAPSHOT", "")
|
||||
version = "$baseVersion-SNAPSHOT+$offset"
|
||||
}
|
||||
|
||||
val changelogText = providers.environmentVariable("CHANGELOG").orElse(providers.exec {
|
||||
commandLine("git", "log", "-1", "--format=%B")
|
||||
}.standardOutput.asText)
|
||||
|
||||
logger.lifecycle("Building version: $version")
|
||||
|
||||
val paperVersions = (property("paperVersion") as String)
|
||||
.split(",")
|
||||
.map { it.trim() }
|
||||
|
||||
val hangarToken = providers.environmentVariable("HANGAR_API_TOKEN")
|
||||
.orElse(providers.gradleProperty("hangarToken"))
|
||||
|
||||
val projectVersion = project.version.toString()
|
||||
val releaseChannel = if (isRelease) "Release" else "Snapshot"
|
||||
val shadowJarTask = tasks.shadowJar
|
||||
|
||||
modrinth {
|
||||
token.set(providers.environmentVariable("MODRINTH_TOKEN"))
|
||||
projectId.set("eternalcombat")
|
||||
versionNumber.set(projectVersion)
|
||||
versionType.set(if (isRelease) "release" else "beta")
|
||||
uploadFile.set(shadowJarTask)
|
||||
gameVersions.addAll(paperVersions)
|
||||
loaders.addAll(listOf("paper", "spigot", "purpur"))
|
||||
changelog.set(changelogText)
|
||||
syncBodyFrom.set(rootProject.file("README.md").readText())
|
||||
}
|
||||
|
||||
|
||||
hangarPublish {
|
||||
publications.register("plugin") {
|
||||
version.set(projectVersion)
|
||||
channel.set(releaseChannel)
|
||||
changelog.set(changelogText)
|
||||
id.set("eternalcombat")
|
||||
apiKey.set(hangarToken)
|
||||
platforms {
|
||||
register(Platforms.PAPER) {
|
||||
jar.set(shadowJarTask.flatMap { it.archiveFile })
|
||||
platformVersions.set(paperVersions)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
resolutionStrategy {
|
||||
eachDependency {
|
||||
if (requested.group == "com.google.code.gson" && requested.name == "gson") {
|
||||
useVersion("2.11.0")
|
||||
because("WorldGuard requires strictly gson 2.11.0")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources.srcDir("src/resources")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":eternalcombat-api"))
|
||||
|
||||
compileOnly("io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT")
|
||||
compileOnly("me.clip:placeholderapi:2.11.6")
|
||||
compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.3.0-SNAPSHOT") // Import WorldEdit.
|
||||
compileOnly(
|
||||
"com.sk89q.worldguard:worldguard-bukkit:7.0.9"
|
||||
) { // Import WorldGuard but without its bundled WorldEdit.
|
||||
exclude(group = "com.sk89q.worldedit")
|
||||
}
|
||||
// kyori
|
||||
implementation("net.kyori:adventure-platform-bukkit:${Versions.ADVENTURE_PLATFORM_BUKKIT}")
|
||||
implementation("net.kyori:adventure-text-minimessage:${Versions.ADVENTURE_API}")
|
||||
implementation("net.kyori:adventure-api") {
|
||||
version {
|
||||
strictly(Versions.ADVENTURE_API)
|
||||
}
|
||||
}
|
||||
// litecommands
|
||||
implementation("dev.rollczi:litecommands-bukkit:${Versions.LITE_COMMANDS}")
|
||||
|
||||
// Okaeri configs
|
||||
implementation("eu.okaeri:okaeri-configs-serdes-commons:${Versions.OKAERI_CONFIGS_SERDES_COMMONS}")
|
||||
implementation("eu.okaeri:okaeri-configs-serdes-bukkit:${Versions.OKAERI_CONFIGS_SERDES_BUKKIT}")
|
||||
|
||||
// XSeries
|
||||
implementation("com.github.cryptomorin:XSeries:${Versions.XSERIES}")
|
||||
// caffeine
|
||||
implementation("com.github.ben-manes.caffeine:caffeine:${Versions.CAFFEINE}")
|
||||
|
||||
implementation("com.eternalcode:eternalcode-commons-adventure:${Versions.ETERNALCODE_COMMONS}")
|
||||
implementation("com.eternalcode:eternalcode-commons-bukkit:${Versions.ETERNALCODE_COMMONS}")
|
||||
implementation("com.eternalcode:eternalcode-commons-shared:${Versions.ETERNALCODE_COMMONS}")
|
||||
implementation("com.eternalcode:eternalcode-commons-updater:${Versions.ETERNALCODE_COMMONS}")
|
||||
|
||||
// worldguard
|
||||
compileOnly("com.sk89q.worldguard:worldguard-bukkit:${Versions.WORLD_GUARD_BUKKIT}")
|
||||
|
||||
// PlaceholderAPI
|
||||
compileOnly("me.clip:placeholderapi:${Versions.PLACEHOLDER_API}")
|
||||
|
||||
// Multification
|
||||
implementation("com.eternalcode:multification-bukkit:${Versions.MULTIFICATION}")
|
||||
implementation("com.eternalcode:multification-okaeri:${Versions.MULTIFICATION}")
|
||||
compileOnly("com.github.retrooper:packetevents-spigot:${Versions.PACKETEVENTS}")
|
||||
implementation("io.papermc:paperlib:${Versions.PAPERLIB}")
|
||||
|
||||
implementation("eu.okaeri:okaeri-configs-core:5.0.9")
|
||||
implementation("eu.okaeri:okaeri-configs-yaml-snakeyaml:5.0.9")
|
||||
implementation("eu.okaeri:okaeri-configs-yaml-bukkit:5.0.9")
|
||||
implementation("eu.okaeri:okaeri-configs-serdes-commons:5.0.9")
|
||||
implementation("eu.okaeri:okaeri-configs-serdes-bukkit:5.0.9")
|
||||
}
|
||||
|
||||
bukkit {
|
||||
name = "EternalCombat-OG"
|
||||
main = "com.eternalcode.combat.CombatPlugin"
|
||||
author = "EternalCodeTeam"
|
||||
apiVersion = "1.13"
|
||||
prefix = "EternalCombat"
|
||||
name = "EternalCombat"
|
||||
softDepend = listOf("WorldGuard")
|
||||
version = project.version.toString()
|
||||
apiVersion = "1.19"
|
||||
authors = listOf("EternalCodeTeam", "true-og")
|
||||
prefix = "EternalCombat-OG"
|
||||
load = BukkitPluginDescription.PluginLoadOrder.POSTWORLD
|
||||
softDepend = listOf(
|
||||
"WorldGuard"
|
||||
)
|
||||
depend = listOf(
|
||||
"PacketEvents",
|
||||
)
|
||||
version = "${project.version}"
|
||||
|
||||
foliaSupported = false
|
||||
}
|
||||
|
||||
tasks {
|
||||
runServer {
|
||||
minecraftVersion("1.19.4")
|
||||
downloadPlugins.modrinth("WorldEdit", Versions.WORLDEDIT)
|
||||
downloadPlugins.modrinth("PacketEvents", "${Versions.PACKETEVENTS}+spigot")
|
||||
downloadPlugins.modrinth("WorldGuard", Versions.WORLDGUARD)
|
||||
downloadPlugins.modrinth("LuckPerms", "v${Versions.LUCKPERMS}-bukkit")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.shadowJar {
|
||||
archiveFileName.set("EternalCombat v${project.version}.jar")
|
||||
|
||||
dependsOn("checkstyleMain")
|
||||
dependsOn("checkstyleTest")
|
||||
dependsOn("test")
|
||||
archiveFileName.set("EternalCombat-OG-${project.version}.jar")
|
||||
|
||||
exclude(
|
||||
"org/intellij/lang/annotations/**",
|
||||
"org/jetbrains/annotations/**",
|
||||
"META-INF/**",
|
||||
"kotlin/**",
|
||||
"javax/**"
|
||||
"javax/**",
|
||||
"org/checkerframework/**",
|
||||
"com/google/errorprone/**"
|
||||
)
|
||||
|
||||
val prefix = "com.eternalcode.combat.libs"
|
||||
listOf(
|
||||
"panda.std",
|
||||
"panda.utilities",
|
||||
"org.panda-lang",
|
||||
"eu.okaeri",
|
||||
"net.kyori",
|
||||
"org.bstats",
|
||||
"org.yaml",
|
||||
"dev.rollczi.litecommands",
|
||||
"com.eternalcode.gitcheck",
|
||||
"org.json.simple",
|
||||
"org.apache.commons",
|
||||
"javassist",
|
||||
"com.github.benmanes.caffeine",
|
||||
"com.eternalcode.commons"
|
||||
"com.eternalcode.commons",
|
||||
"com.eternalcode.multification",
|
||||
"com.github.cryptomorin",
|
||||
"io.papermc.lib",
|
||||
).forEach { pack ->
|
||||
relocate(pack, "$prefix.$pack")
|
||||
}
|
||||
|
||||
@@ -1,99 +1,127 @@
|
||||
package com.eternalcode.combat;
|
||||
|
||||
import com.eternalcode.combat.border.BorderTriggerController;
|
||||
import com.eternalcode.combat.border.BorderService;
|
||||
import com.eternalcode.combat.border.BorderServiceImpl;
|
||||
import com.eternalcode.combat.border.animation.block.BorderBlockController;
|
||||
import com.eternalcode.combat.border.animation.particle.ParticleController;
|
||||
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.crystalpvp.EndCrystalListener;
|
||||
import com.eternalcode.combat.crystalpvp.RespawnAnchorListener;
|
||||
import com.eternalcode.combat.event.EventManager;
|
||||
import com.eternalcode.combat.fight.FightManager;
|
||||
import com.eternalcode.combat.fight.FightManagerImpl;
|
||||
import com.eternalcode.combat.fight.FightTagCommand;
|
||||
import com.eternalcode.combat.fight.FightTask;
|
||||
import com.eternalcode.combat.fight.controller.FightActionBlockerController;
|
||||
import com.eternalcode.combat.fight.controller.FightBypassAdminController;
|
||||
import com.eternalcode.combat.fight.controller.FightBypassCreativeController;
|
||||
import com.eternalcode.combat.fight.controller.FightBypassPermissionController;
|
||||
import com.eternalcode.combat.fight.controller.FightInventoryController;
|
||||
import com.eternalcode.combat.fight.death.DeathFlareController;
|
||||
import com.eternalcode.combat.fight.death.DeathLightningController;
|
||||
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.drop.DropController;
|
||||
import com.eternalcode.combat.fight.drop.DropKeepInventoryService;
|
||||
import com.eternalcode.combat.fight.drop.DropKeepInventoryServiceImpl;
|
||||
import com.eternalcode.combat.fight.drop.DropService;
|
||||
import com.eternalcode.combat.fight.drop.DropServiceImpl;
|
||||
import com.eternalcode.combat.fight.drop.impl.PercentDropModifier;
|
||||
import com.eternalcode.combat.fight.drop.impl.PlayersHealthDropModifier;
|
||||
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.effect.FightEffectServiceImpl;
|
||||
import com.eternalcode.combat.fight.firework.FireworkController;
|
||||
import com.eternalcode.combat.fight.knockback.KnockbackRegionController;
|
||||
import com.eternalcode.combat.fight.knockback.KnockbackService;
|
||||
import com.eternalcode.combat.fight.logout.LogoutController;
|
||||
import com.eternalcode.combat.fight.logout.LogoutService;
|
||||
import com.eternalcode.combat.fight.logout.OpponentLogoutController;
|
||||
import com.eternalcode.combat.fight.pearl.FightPearlController;
|
||||
import com.eternalcode.combat.fight.pearl.FightPearlManager;
|
||||
import com.eternalcode.combat.fight.pearl.FightPearlService;
|
||||
import com.eternalcode.combat.fight.pearl.FightPearlServiceImpl;
|
||||
import com.eternalcode.combat.fight.tagout.FightTagOutCommand;
|
||||
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.fight.tagout.FightTagOutServiceImpl;
|
||||
import com.eternalcode.combat.handler.InvalidUsageHandlerImpl;
|
||||
import com.eternalcode.combat.handler.MissingPermissionHandlerImpl;
|
||||
import com.eternalcode.combat.notification.NoticeService;
|
||||
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.eternalcode.commons.bukkit.scheduler.MinecraftScheduler;
|
||||
import com.eternalcode.multification.notice.Notice;
|
||||
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 dev.rollczi.litecommands.bukkit.LiteBukkitFactory;
|
||||
import dev.rollczi.litecommands.bukkit.LiteBukkitMessages;
|
||||
import java.io.File;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Stream;
|
||||
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.event.entity.PlayerDeathEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
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 static final String CONFIG_RESOURCE_PATH = "config.yml";
|
||||
|
||||
private FightManager fightManager;
|
||||
private FightPearlManager fightPearlManager;
|
||||
private FightPearlService fightPearlService;
|
||||
private FightTagOutService fightTagOutService;
|
||||
private FightEffectService fightEffectService;
|
||||
|
||||
private LogoutService logoutService;
|
||||
|
||||
private DropManager dropManager;
|
||||
private DropKeepInventoryManager dropKeepInventoryManager;
|
||||
private DropService dropService;
|
||||
private DropKeepInventoryService dropKeepInventoryService;
|
||||
|
||||
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();
|
||||
File configFile = new File(dataFolder, CONFIG_RESOURCE_PATH);
|
||||
|
||||
if (!configFile.exists()) {
|
||||
this.saveResource(CONFIG_RESOURCE_PATH, false);
|
||||
}
|
||||
|
||||
ConfigService configService = new ConfigService();
|
||||
|
||||
EventCaller eventCaller = new EventCaller(server);
|
||||
EventManager eventManager = new EventManager(this);
|
||||
PluginConfig pluginConfig = configService.create(PluginConfig.class, configFile);
|
||||
|
||||
this.pluginConfig = configService.create(PluginConfig.class, new File(dataFolder, "config.yml"));
|
||||
MinecraftScheduler scheduler = CombatSchedulerAdapter.getAdaptiveScheduler(this);
|
||||
|
||||
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();
|
||||
this.fightManager = new FightManagerImpl(eventManager);
|
||||
this.fightPearlService = new FightPearlServiceImpl(pluginConfig.pearl);
|
||||
this.fightTagOutService = new FightTagOutServiceImpl();
|
||||
this.fightEffectService = new FightEffectServiceImpl();
|
||||
|
||||
LogoutService logoutService = new LogoutService();
|
||||
|
||||
this.dropService = new DropServiceImpl();
|
||||
this.dropKeepInventoryService = new DropKeepInventoryServiceImpl();
|
||||
|
||||
UpdaterService updaterService = new UpdaterService(this.getDescription());
|
||||
|
||||
@@ -103,49 +131,85 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
|
||||
.preProcessor(new AdventureLegacyColorPreProcessor())
|
||||
.build();
|
||||
|
||||
FightBossBarService fightBossBarService = new FightBossBarService(this.pluginConfig, this.audienceProvider, miniMessage);
|
||||
NoticeService noticeService = new NoticeService(this.audienceProvider, pluginConfig, miniMessage);
|
||||
|
||||
BridgeService bridgeService = new BridgeService(
|
||||
pluginConfig,
|
||||
server.getPluginManager(),
|
||||
this.getLogger(),
|
||||
this,
|
||||
this.fightManager
|
||||
);
|
||||
bridgeService.init(server);
|
||||
|
||||
BridgeService bridgeService = new BridgeService(this.pluginConfig, server.getPluginManager(), this.getLogger());
|
||||
bridgeService.init();
|
||||
this.regionProvider = bridgeService.getRegionProvider();
|
||||
BorderService borderService = new BorderServiceImpl(scheduler, server, regionProvider, eventManager, () -> pluginConfig.border);
|
||||
KnockbackService knockbackService = new KnockbackService(pluginConfig, scheduler, regionProvider);
|
||||
|
||||
NotificationAnnouncer notificationAnnouncer = new NotificationAnnouncer(this.audienceProvider, miniMessage);
|
||||
this.liteCommands = LiteBukkitFactory.builder(FALLBACK_PREFIX, this, server)
|
||||
.message(LiteBukkitMessages.PLAYER_NOT_FOUND, pluginConfig.messagesSettings.playerNotFound)
|
||||
.message(LiteBukkitMessages.PLAYER_ONLY, pluginConfig.messagesSettings.admin.onlyForPlayers)
|
||||
|
||||
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))
|
||||
.invalidUsage(new InvalidUsageHandlerImpl(pluginConfig, noticeService))
|
||||
.missingPermission(new MissingPermissionHandlerImpl(pluginConfig, noticeService))
|
||||
|
||||
.invalidUsageHandler(new InvalidUsage(this.pluginConfig, notificationAnnouncer))
|
||||
.permissionHandler(new PermissionMessage(this.pluginConfig, notificationAnnouncer))
|
||||
.commands(
|
||||
new FightTagCommand(this.fightManager, noticeService, pluginConfig),
|
||||
new FightTagOutCommand(this.fightTagOutService, noticeService, pluginConfig),
|
||||
new EternalCombatReloadCommand(configService, noticeService)
|
||||
)
|
||||
|
||||
.commandInstance(new CombatCommand(this.fightManager, configService, notificationAnnouncer, this.pluginConfig))
|
||||
.commandInstance(new TagOutCommand(this.fightTagOutService, notificationAnnouncer, this.pluginConfig))
|
||||
.result(Notice.class, (invocation, result, chain) -> noticeService.create()
|
||||
.viewer(invocation.sender())
|
||||
.notice(result)
|
||||
.send())
|
||||
|
||||
.register();
|
||||
.build();
|
||||
|
||||
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);
|
||||
FightTask fightTask = new FightTask(server, pluginConfig, this.fightManager, noticeService);
|
||||
scheduler.timer(fightTask, Duration.ofSeconds(1), Duration.ofSeconds(1));
|
||||
|
||||
Stream.of(
|
||||
new PercentDropModifier(this.pluginConfig.dropSettings),
|
||||
new PlayersHealthDropModifier(this.pluginConfig.dropSettings, this.logoutService)
|
||||
).forEach(this.dropManager::registerModifier);
|
||||
new PercentDropModifier(pluginConfig.drop),
|
||||
new PlayersHealthDropModifier(pluginConfig.drop, logoutService)
|
||||
).forEach(this.dropService::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));
|
||||
eventManager.subscribe(
|
||||
new FightTagController(this.fightManager, pluginConfig),
|
||||
new FightUnTagController(this.fightManager, pluginConfig, logoutService),
|
||||
new FightBypassAdminController(server, pluginConfig),
|
||||
new FightBypassPermissionController(server, pluginConfig),
|
||||
new FightBypassCreativeController(server, pluginConfig),
|
||||
new FightActionBlockerController(this.fightManager, noticeService, pluginConfig, server),
|
||||
new FightPearlController(pluginConfig.pearl, noticeService, this.fightManager, this.fightPearlService),
|
||||
new DeathFlareController(pluginConfig, server, scheduler, this),
|
||||
new DeathLightningController(pluginConfig, server),
|
||||
new UpdaterNotificationController(updaterService, pluginConfig, this.audienceProvider, miniMessage),
|
||||
new KnockbackRegionController(noticeService, this.regionProvider, this.fightManager, knockbackService, server),
|
||||
new FightEffectController(pluginConfig.effect, this.fightEffectService, this.fightManager, server),
|
||||
new FightTagOutController(this.fightTagOutService),
|
||||
new OpponentLogoutController(this.fightManager, server),
|
||||
new FightMessageController(this.fightManager, noticeService, pluginConfig, server),
|
||||
new BorderTriggerController(borderService, () -> pluginConfig.border, fightManager, server, scheduler),
|
||||
new ParticleController(borderService, () -> pluginConfig.border.particle, scheduler, server),
|
||||
new BorderBlockController(borderService, () -> pluginConfig.border.block, scheduler, server),
|
||||
new EndCrystalListener(this, this.fightManager, pluginConfig),
|
||||
new RespawnAnchorListener(this, this.fightManager, pluginConfig),
|
||||
new FireworkController(this.fightManager, pluginConfig, noticeService),
|
||||
new FightInventoryController(this.fightManager, pluginConfig, noticeService)
|
||||
);
|
||||
|
||||
eventManager.subscribe(
|
||||
PlayerDeathEvent.class,
|
||||
pluginConfig.drop.dropEventPriority,
|
||||
new DropController(dropService, dropKeepInventoryService, pluginConfig.drop, fightManager, miniMessage)
|
||||
);
|
||||
|
||||
eventManager.subscribe(
|
||||
PlayerQuitEvent.class,
|
||||
pluginConfig.combat.quitPunishmentEventPriority,
|
||||
new LogoutController(this.fightManager, logoutService, noticeService, pluginConfig)
|
||||
);
|
||||
|
||||
EternalCombatProvider.initialize(this);
|
||||
|
||||
@@ -155,19 +219,22 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
EternalCombatProvider.deinitialize();
|
||||
if (EternalCombatProvider.isInitialized()) {
|
||||
EternalCombatProvider.deinitialize();
|
||||
}
|
||||
|
||||
if (this.liteCommands != null) {
|
||||
this.liteCommands.getPlatform().unregisterAll();
|
||||
this.liteCommands.unregister();
|
||||
}
|
||||
|
||||
if (this.audienceProvider != null) {
|
||||
this.audienceProvider.close();
|
||||
}
|
||||
|
||||
this.fightManager.untagAll();
|
||||
if (this.fightManager != null) {
|
||||
this.fightManager.untagAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FightManager getFightManager() {
|
||||
return this.fightManager;
|
||||
@@ -179,8 +246,8 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FightPearlManager getFightPearlManager() {
|
||||
return this.fightPearlManager;
|
||||
public FightPearlService getFightPearlService() {
|
||||
return this.fightPearlService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -194,17 +261,12 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DropManager getDropManager() {
|
||||
return this.dropManager;
|
||||
public DropService getDropService() {
|
||||
return this.dropService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DropKeepInventoryManager getDropKeepInventoryManager() {
|
||||
return this.dropKeepInventoryManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginConfig getPluginConfig() {
|
||||
return this.pluginConfig;
|
||||
public DropKeepInventoryService getDropKeepInventoryService() {
|
||||
return this.dropKeepInventoryService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.eternalcode.combat;
|
||||
|
||||
import com.eternalcode.commons.bukkit.scheduler.BukkitSchedulerImpl;
|
||||
import com.eternalcode.commons.bukkit.scheduler.MinecraftScheduler;
|
||||
import java.util.logging.Logger;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public final class CombatSchedulerAdapter {
|
||||
|
||||
private CombatSchedulerAdapter() {
|
||||
throw new UnsupportedOperationException("This is an utility class and cannot be instantiated");
|
||||
}
|
||||
|
||||
public static MinecraftScheduler getAdaptiveScheduler(Plugin plugin) {
|
||||
Logger logger = plugin.getLogger();
|
||||
logger.info("» Detected Bukkit/Paper environment. Using BukkitScheduler.");
|
||||
return new BukkitSchedulerImpl(plugin);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.eternalcode.combat;
|
||||
|
||||
import com.eternalcode.combat.config.ConfigService;
|
||||
import com.eternalcode.combat.notification.NoticeService;
|
||||
import com.eternalcode.multification.bukkit.notice.BukkitNotice;
|
||||
import com.eternalcode.multification.notice.Notice;
|
||||
import com.google.common.base.Stopwatch;
|
||||
import dev.rollczi.litecommands.annotations.async.Async;
|
||||
import dev.rollczi.litecommands.annotations.command.Command;
|
||||
import dev.rollczi.litecommands.annotations.context.Context;
|
||||
import dev.rollczi.litecommands.annotations.execute.Execute;
|
||||
import dev.rollczi.litecommands.annotations.permission.Permission;
|
||||
import java.time.Duration;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
@Command(name = "combatlog", aliases = "combat")
|
||||
public class EternalCombatReloadCommand {
|
||||
|
||||
private static final Notice RELOAD_MESSAGE = BukkitNotice.builder()
|
||||
.chat("<b><gradient:#8a1212:#fc6b03>EternalCombat:</gradient></b> Reloaded EternalCombat in <color:#fce303>{TIME}ms!</color>")
|
||||
.build();
|
||||
|
||||
private final ConfigService configService;
|
||||
private final NoticeService noticeService;
|
||||
|
||||
public EternalCombatReloadCommand(ConfigService configService, NoticeService noticeService) {
|
||||
this.configService = configService;
|
||||
this.noticeService = noticeService;
|
||||
}
|
||||
|
||||
@Async
|
||||
@Execute(name = "reload")
|
||||
@Permission("eternalcombat.reload")
|
||||
void execute(@Context CommandSender sender) {
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
this.configService.reload();
|
||||
|
||||
Duration elapsed = stopwatch.elapsed();
|
||||
this.noticeService.create()
|
||||
.viewer(sender)
|
||||
.notice(RELOAD_MESSAGE)
|
||||
.placeholder("{TIME}", String.valueOf(elapsed.toMillis()))
|
||||
.send();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.eternalcode.combat.border;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
class BorderActivePointsIndex {
|
||||
|
||||
private final Map<String, Map<UUID, Set<BorderPoint>>> registry = new ConcurrentHashMap<>();
|
||||
|
||||
boolean hasPoints(String world, UUID player) {
|
||||
Map<UUID, Set<BorderPoint>> worldRegistry = this.registry.get(world);
|
||||
if (worldRegistry == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return worldRegistry.containsKey(player);
|
||||
}
|
||||
|
||||
Set<BorderPoint> putPoints(String world, UUID player, Set<BorderPoint> points) {
|
||||
Map<UUID, Set<BorderPoint>> worldRegistry = this.registry.computeIfAbsent(world, k -> new ConcurrentHashMap<>());
|
||||
Set<BorderPoint> oldPoints = worldRegistry.put(player, points);
|
||||
if (oldPoints != null) {
|
||||
return calculateRemovedPoints(points, oldPoints);
|
||||
}
|
||||
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
private static Set<BorderPoint> calculateRemovedPoints(Set<BorderPoint> points, Set<BorderPoint> oldPoints) {
|
||||
Set<BorderPoint> removed = new HashSet<>();
|
||||
for (BorderPoint oldPoint : oldPoints) {
|
||||
if (!points.contains(oldPoint)) {
|
||||
removed.add(oldPoint);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableSet(removed);
|
||||
}
|
||||
|
||||
Set<BorderPoint> getPoints(String world, UUID player) {
|
||||
Map<UUID, Set<BorderPoint>> worldRegistry = this.registry.get(world);
|
||||
if (worldRegistry == null) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
return worldRegistry.getOrDefault(player, Set.of());
|
||||
}
|
||||
|
||||
Set<BorderPoint> removePoints(String world, UUID player) {
|
||||
Map<UUID, Set<BorderPoint>> worldRegistry = this.registry.get(world);
|
||||
if (worldRegistry == null) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
Set<BorderPoint> remove = worldRegistry.remove(player);
|
||||
if (remove == null) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
return remove;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.eternalcode.combat.border;
|
||||
|
||||
import dev.rollczi.litecommands.shared.Lazy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class BorderLazyResult implements BorderResult {
|
||||
|
||||
private final List<Lazy<List<BorderPoint>>> borderPoints = new ArrayList<>();
|
||||
|
||||
void addLazyBorderPoints(Lazy<List<BorderPoint>> supplier) {
|
||||
borderPoints.add(supplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<BorderPoint> collect() {
|
||||
return borderPoints.stream()
|
||||
.flatMap(result -> result.get().stream())
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.eternalcode.combat.border;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public record BorderPoint(int x, int y, int z, @Nullable BorderPoint innerPoint) {
|
||||
|
||||
public BorderPoint(int x, int y, int z) {
|
||||
this(x, y, z, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* This point represents an adjusted coordinate that takes into account the block's volume (thickness)
|
||||
* and is used for logical placement and grouping, especially for assigning border blocks to the
|
||||
* correct chunk when they are on the very edge of a chunk boundary.
|
||||
* This ensures the block is considered to be on the "inner" side of the border for chunk-based logic.
|
||||
*/
|
||||
@Override
|
||||
public BorderPoint innerPoint() {
|
||||
if (this.innerPoint == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return this.innerPoint;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.eternalcode.combat.border;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface BorderResult {
|
||||
|
||||
Set<BorderPoint> collect();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.eternalcode.combat.border;
|
||||
|
||||
import java.util.Set;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface BorderService {
|
||||
|
||||
void updateBorder(Player player, Location to);
|
||||
|
||||
void clearBorder(Player player);
|
||||
|
||||
Set<BorderPoint> getActiveBorder(Player player);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package com.eternalcode.combat.border;
|
||||
|
||||
import com.eternalcode.combat.border.event.BorderHideAsyncEvent;
|
||||
import com.eternalcode.combat.border.event.BorderShowAsyncEvent;
|
||||
import com.eternalcode.combat.event.EventManager;
|
||||
import com.eternalcode.combat.region.Region;
|
||||
import com.eternalcode.combat.region.RegionProvider;
|
||||
import com.eternalcode.commons.bukkit.scheduler.MinecraftScheduler;
|
||||
import dev.rollczi.litecommands.shared.Lazy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class BorderServiceImpl implements BorderService {
|
||||
|
||||
// Horizontal neighbour offsets (west, east, north, south). The border is a vertical wall, so only
|
||||
// horizontal transitions between safe and unsafe space matter.
|
||||
private static final int[][] HORIZONTAL_NEIGHBOURS = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
|
||||
|
||||
private final MinecraftScheduler scheduler;
|
||||
private final EventManager eventManager;
|
||||
private final RegionProvider regionProvider;
|
||||
|
||||
private final Supplier<BorderSettings> settings;
|
||||
|
||||
private final BorderTriggerIndex borderIndexes;
|
||||
private final BorderActivePointsIndex activeBorderIndex = new BorderActivePointsIndex();
|
||||
|
||||
public BorderServiceImpl(MinecraftScheduler scheduler, Server server, RegionProvider provider, EventManager eventManager, Supplier<BorderSettings> settings) {
|
||||
this.scheduler = scheduler;
|
||||
this.eventManager = eventManager;
|
||||
this.regionProvider = provider;
|
||||
this.settings = settings;
|
||||
this.borderIndexes = BorderTriggerIndex.started(server, scheduler, provider, settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBorder(Player player, Location location) {
|
||||
Optional<BorderResult> result = resolveBorder(location);
|
||||
String world = player.getWorld().getName();
|
||||
|
||||
if (result.isEmpty()) {
|
||||
if (!this.activeBorderIndex.hasPoints(world, player.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.clearBorder(player);
|
||||
return;
|
||||
}
|
||||
|
||||
scheduler.runAsync(() -> {
|
||||
BorderResult borderResult = result.get();
|
||||
Set<BorderPoint> points = borderResult.collect();
|
||||
|
||||
if (!points.isEmpty()) {
|
||||
BorderShowAsyncEvent event = eventManager.publishEvent(new BorderShowAsyncEvent(player, points));
|
||||
points = event.getPoints();
|
||||
}
|
||||
|
||||
Set<BorderPoint> removed = this.activeBorderIndex.putPoints(world, player.getUniqueId(), points);
|
||||
|
||||
if (!removed.isEmpty()) {
|
||||
eventManager.publishEvent(new BorderHideAsyncEvent(player, removed));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearBorder(Player player) {
|
||||
World world = player.getWorld();
|
||||
UUID uniqueId = player.getUniqueId();
|
||||
|
||||
scheduler.runAsync(() -> {
|
||||
Set<BorderPoint> removed = this.activeBorderIndex.removePoints(world.getName(), uniqueId);
|
||||
if (!removed.isEmpty()) {
|
||||
eventManager.publishEvent(new BorderHideAsyncEvent(player, removed));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<BorderPoint> getActiveBorder(Player player) {
|
||||
return this.activeBorderIndex.getPoints(player.getWorld().getName(), player.getUniqueId());
|
||||
}
|
||||
|
||||
private Optional<BorderResult> resolveBorder(Location location) {
|
||||
// Cheap gate: only build a border when the player is near a restricted region at all.
|
||||
if (borderIndexes.getTriggered(location).isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
BorderLazyResult result = new BorderLazyResult();
|
||||
result.addLazyBorderPoints(new Lazy<>(() -> this.resolveBorderPoints(location)));
|
||||
return Optional.of(result);
|
||||
}
|
||||
|
||||
// Render glass on the true boundary between safe (PvP-denied) and unsafe (PvP-allowed/wilderness)
|
||||
// space, rather than around a region's bounding box. A wall block is placed on a safe column only
|
||||
// when an adjacent column is unsafe, so the wall appears between the warzone and the safe spawn or
|
||||
// market, but never between the warzone and the wilderness (both unsafe), and never on the outer
|
||||
// face of a large umbrella region that merely encloses the warzone.
|
||||
private List<BorderPoint> resolveBorderPoints(Location playerLocation) {
|
||||
World world = playerLocation.getWorld();
|
||||
int px = (int) Math.round(playerLocation.getX());
|
||||
int py = (int) Math.round(playerLocation.getY());
|
||||
int pz = (int) Math.round(playerLocation.getZ());
|
||||
int distance = settings.get().distanceRounded();
|
||||
|
||||
// Sample, per column near the player, the safe region present at the player's Y (null = unsafe).
|
||||
// Sampling one Y keeps this cheap; safe regions are effectively full-height in practice.
|
||||
Map<Long, Region> columns = new HashMap<>();
|
||||
for (int dx = -distance - 1; dx <= distance + 1; dx++) {
|
||||
for (int dz = -distance - 1; dz <= distance + 1; dz++) {
|
||||
int cx = px + dx;
|
||||
int cz = pz + dz;
|
||||
Region region = this.regionProvider.getRegion(new Location(world, cx, py, cz)).orElse(null);
|
||||
columns.put(key(cx, cz), region);
|
||||
}
|
||||
}
|
||||
|
||||
List<BorderPoint> points = new ArrayList<>();
|
||||
for (int dx = -distance; dx <= distance; dx++) {
|
||||
for (int dz = -distance; dz <= distance; dz++) {
|
||||
int cx = px + dx;
|
||||
int cz = pz + dz;
|
||||
|
||||
// Only unsafe columns (where the player can stand) cast a wall onto safe neighbours.
|
||||
if (columns.get(key(cx, cz)) != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int[] neighbour : HORIZONTAL_NEIGHBOURS) {
|
||||
int nx = cx + neighbour[0];
|
||||
int nz = cz + neighbour[1];
|
||||
|
||||
Region safeRegion = columns.get(key(nx, nz));
|
||||
if (safeRegion == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int minY = Math.max(py - distance, safeRegion.getMin().getBlockY());
|
||||
int maxY = Math.min(py + distance, safeRegion.getMax().getBlockY());
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
if (isVisible(nx, y, nz, playerLocation)) {
|
||||
points.add(new BorderPoint(nx, y, nz));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
private static long key(int x, int z) {
|
||||
return (((long) x) << 32) ^ (z & 0xffffffffL);
|
||||
}
|
||||
|
||||
private boolean isVisible(int x, int y, int z, Location player) {
|
||||
return Math.sqrt(Math.pow(x - player.getX(), 2) + Math.pow(y - player.getY(), 2) + Math.pow(z - player.getZ(), 2)) <= this.settings.get().distance;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.eternalcode.combat.border;
|
||||
|
||||
import com.eternalcode.combat.border.animation.block.BlockSettings;
|
||||
import com.eternalcode.combat.border.animation.particle.ParticleSettings;
|
||||
import eu.okaeri.configs.OkaeriConfig;
|
||||
import eu.okaeri.configs.annotation.Comment;
|
||||
import java.time.Duration;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
public class BorderSettings extends OkaeriConfig {
|
||||
|
||||
@Comment("# Border view distance in blocks")
|
||||
public double distance = 6.5;
|
||||
|
||||
@Comment({
|
||||
" ",
|
||||
"# Border block animation settings",
|
||||
"# Configure the block animation that appears during combat."
|
||||
})
|
||||
public BlockSettings block = new BlockSettings();
|
||||
|
||||
@Comment({
|
||||
" ",
|
||||
"# Border particle animation settings",
|
||||
"# Configure the particle animation that appears during combat."
|
||||
})
|
||||
public ParticleSettings particle = new ParticleSettings();
|
||||
|
||||
@ApiStatus.Internal
|
||||
public Duration indexRefreshDelay() {
|
||||
return Duration.ofSeconds(1);
|
||||
}
|
||||
|
||||
public int distanceRounded() {
|
||||
return (int) Math.ceil(this.distance);
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.block.enabled || this.particle.enabled;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.eternalcode.combat.border;
|
||||
|
||||
record BorderTrigger(BorderPoint min, BorderPoint max, BorderPoint triggerMin, BorderPoint triggerMax) {
|
||||
|
||||
BorderTrigger(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, int distance) {
|
||||
this(
|
||||
new BorderPoint(minX, minY, minZ),
|
||||
new BorderPoint(maxX, maxY, maxZ),
|
||||
new BorderPoint(minX - distance, minY - distance, minZ - distance),
|
||||
new BorderPoint(maxX + distance, maxY + distance, maxZ + distance)
|
||||
);
|
||||
}
|
||||
|
||||
public boolean isTriggered(int x, int y, int z) {
|
||||
return x >= triggerMin.x() && x <= triggerMax.x()
|
||||
&& y >= triggerMin.y() && y <= triggerMax.y()
|
||||
&& z >= triggerMin.z() && z <= triggerMax.z();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.eternalcode.combat.border;
|
||||
|
||||
import com.eternalcode.combat.fight.FightManager;
|
||||
import com.eternalcode.combat.fight.event.FightTagEvent;
|
||||
import com.eternalcode.combat.fight.event.FightUntagEvent;
|
||||
import com.eternalcode.commons.bukkit.scheduler.MinecraftScheduler;
|
||||
import java.time.Duration;
|
||||
import java.util.function.Supplier;
|
||||
import org.bukkit.Location;
|
||||
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.player.PlayerMoveEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
|
||||
public class BorderTriggerController implements Listener {
|
||||
|
||||
private static final long BORDER_WORK_AROUND_DELAY_MILLIS = 250;
|
||||
|
||||
private final BorderService borderService;
|
||||
private final Supplier<BorderSettings> border;
|
||||
private final FightManager fightManager;
|
||||
private final Server server;
|
||||
private final MinecraftScheduler scheduler;
|
||||
|
||||
public BorderTriggerController(
|
||||
BorderService borderService,
|
||||
Supplier<BorderSettings> border,
|
||||
FightManager fightManager,
|
||||
Server server,
|
||||
MinecraftScheduler scheduler
|
||||
) {
|
||||
this.borderService = borderService;
|
||||
this.border = border;
|
||||
this.fightManager = fightManager;
|
||||
this.server = server;
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
void onMove(PlayerMoveEvent event) {
|
||||
if (!border.get().isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Location to = event.getTo();
|
||||
Location from = event.getFrom();
|
||||
if (to.getBlockX() == from.getBlockX() && to.getBlockY() == from.getBlockY() && to.getBlockZ() == from.getBlockZ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = event.getPlayer();
|
||||
if (!fightManager.isInCombat(player.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
borderService.updateBorder(player, to);
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
void onTeleport(PlayerTeleportEvent event) {
|
||||
if (!border.get().isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = event.getPlayer();
|
||||
if (!fightManager.isInCombat(player.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
borderService.updateBorder(player, event.getTo());
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
void onFightStart(FightTagEvent event) {
|
||||
if (!border.get().isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = server.getPlayer(event.getPlayer());
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
borderService.updateBorder(player, player.getLocation());
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
void onFightEnd(FightUntagEvent event) {
|
||||
if (!border.get().isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = server.getPlayer(event.getPlayer());
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Due to race condition in PlayerMoveEvent and FightUntagEvent - border can be triggered after death and untag of player - solution delayed removal of border
|
||||
this.scheduler.runLater(() -> {
|
||||
if (!this.fightManager.isInCombat(player.getUniqueId())) {
|
||||
this.borderService.clearBorder(player);
|
||||
}
|
||||
}, Duration.ofMillis(BORDER_WORK_AROUND_DELAY_MILLIS));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.eternalcode.combat.border;
|
||||
|
||||
import com.eternalcode.combat.region.Region;
|
||||
import com.eternalcode.combat.region.RegionProvider;
|
||||
import com.eternalcode.commons.bukkit.scheduler.MinecraftScheduler;
|
||||
import com.eternalcode.commons.scheduler.Scheduler;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
|
||||
class BorderTriggerIndex {
|
||||
|
||||
private final Server server;
|
||||
private final MinecraftScheduler scheduler;
|
||||
private final RegionProvider provider;
|
||||
private final Supplier<BorderSettings> settings;
|
||||
|
||||
private final Map<String, BorderTriggerIndexBucket> borderIndexes = new HashMap<>();
|
||||
|
||||
private BorderTriggerIndex(Server server, MinecraftScheduler scheduler, RegionProvider provider, Supplier<BorderSettings> settings) {
|
||||
this.server = server;
|
||||
this.scheduler = scheduler;
|
||||
this.provider = provider;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
private void updateWorlds() {
|
||||
for (World world : server.getWorlds()) {
|
||||
this.updateWorld(world.getName(), provider.getRegions(world));
|
||||
}
|
||||
}
|
||||
|
||||
private void updateWorld(String world, Collection<Region> regions) {
|
||||
this.scheduler.runAsync(() -> {
|
||||
int distanceRounded = settings.get().distanceRounded();
|
||||
List<BorderTrigger> triggers = new ArrayList<>();
|
||||
for (Region region : regions) {
|
||||
Location min = region.getMin();
|
||||
Location max = region.getMax();
|
||||
|
||||
triggers.add(new BorderTrigger(
|
||||
min.getBlockX(), min.getBlockY(), min.getBlockZ(),
|
||||
max.getBlockX() + 1, max.getBlockY() + 1, max.getBlockZ() + 1,
|
||||
distanceRounded
|
||||
));
|
||||
}
|
||||
|
||||
BorderTriggerIndexBucket index = BorderTriggerIndexBucket.create(triggers);
|
||||
this.borderIndexes.put(world, index);
|
||||
});
|
||||
}
|
||||
|
||||
static BorderTriggerIndex started(Server server, MinecraftScheduler scheduler, RegionProvider provider, Supplier<BorderSettings> settings) {
|
||||
BorderTriggerIndex index = new BorderTriggerIndex(server, scheduler, provider, settings);
|
||||
scheduler.timerAsync(() -> index.updateWorlds(), Duration.ZERO, settings.get().indexRefreshDelay());
|
||||
return index;
|
||||
}
|
||||
|
||||
public List<BorderTrigger> getTriggered(Location location) {
|
||||
BorderTriggerIndexBucket index = borderIndexes.get(location.getWorld().getName());
|
||||
if (index == null) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
int x = (int) Math.round(location.getX());
|
||||
int y = (int) Math.round(location.getY());
|
||||
int z = (int) Math.round(location.getZ());
|
||||
|
||||
return index.getTriggers(x, z)
|
||||
.stream()
|
||||
.filter(trigger -> trigger.isTriggered(x, y, z))
|
||||
.toList();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.eternalcode.combat.border;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
class BorderTriggerIndexBucket {
|
||||
|
||||
/**
|
||||
* Represents "00000000 00000000 00000000 00000000 11111111 11111111 11111111 11111111" bit mask.
|
||||
* This allows removing left bits when used with AND bit operator.
|
||||
* */
|
||||
private static final long LEFT_INT_MASK = 0xFFFFFFFFL;
|
||||
|
||||
/**
|
||||
* Allows packing the coordination on one axis from 256 into 1
|
||||
* Why? - We divide the world into fragments of size 256x256
|
||||
* Why it is 8? Because when you shift bits, then the value will be smaller / bigger in scale 2^n
|
||||
* E.g `2^4 = 16` (standard minecraft chunk size) in our case, it is `2^8 = 256`
|
||||
*/
|
||||
private static final int CHUNK_SHIFT = 8;
|
||||
|
||||
private final Map<Long, Set<BorderTrigger>> index = new HashMap<>();
|
||||
|
||||
private BorderTriggerIndexBucket() {
|
||||
}
|
||||
|
||||
Set<BorderTrigger> getTriggers(int x, int z) {
|
||||
long position = packChunk(x >> CHUNK_SHIFT, z >> CHUNK_SHIFT);
|
||||
return this.index.getOrDefault(position, Set.of());
|
||||
}
|
||||
|
||||
static BorderTriggerIndexBucket create(Collection<BorderTrigger> triggers) {
|
||||
return new BorderTriggerIndexBucket().with(triggers);
|
||||
}
|
||||
|
||||
private BorderTriggerIndexBucket with(Collection<BorderTrigger> triggers) {
|
||||
for (BorderTrigger trigger : triggers) {
|
||||
withTrigger(trigger);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void withTrigger(BorderTrigger trigger) {
|
||||
int minX = trigger.triggerMin().x() >> CHUNK_SHIFT;
|
||||
int minZ = trigger.triggerMin().z() >> CHUNK_SHIFT;
|
||||
int maxX = trigger.triggerMax().x() >> CHUNK_SHIFT;
|
||||
int maxZ = trigger.triggerMax().z() >> CHUNK_SHIFT;
|
||||
|
||||
int startX = Math.min(minX, maxX);
|
||||
int startZ = Math.min(minZ, maxZ);
|
||||
int endX = Math.max(minX, maxX);
|
||||
int endZ = Math.max(minZ, maxZ);
|
||||
|
||||
for (int chunkX = startX; chunkX <= endX; chunkX++) {
|
||||
for (int chunkZ = startZ; chunkZ <= endZ; chunkZ++) {
|
||||
long packed = packChunk(chunkX, chunkZ);
|
||||
this.index.computeIfAbsent(packed, key -> new HashSet<>())
|
||||
.add(trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Packs two ints into long value.
|
||||
* <p>
|
||||
* For example for values:
|
||||
* <ul>
|
||||
* <li>X - 00000000 00000000 00000000 00000001 </li>
|
||||
* <li>Z - 00000000 00000000 00000000 00000111 </li>
|
||||
* </ul>
|
||||
* This method will return:<br>
|
||||
* 00000000 00000000 00000000 00000111 00000000 00000000 00000000 00000001
|
||||
* </p>
|
||||
* @param bigChunkX right int to pack
|
||||
* @param bigChunkZ left int to pack
|
||||
*/
|
||||
private static long packChunk(int bigChunkX, int bigChunkZ) {
|
||||
return (long) bigChunkX & LEFT_INT_MASK | ((long) bigChunkZ & LEFT_INT_MASK) << Integer.SIZE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.eternalcode.combat.border.animation;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
public final class BorderColorUtil {
|
||||
|
||||
private BorderColorUtil() {
|
||||
}
|
||||
|
||||
public static Color xyzToRainbow(int x, int y, int z) {
|
||||
float hue = (float) ((Math.sin(x * 0.05) + Math.cos(z * 0.05)) * 0.5 + 0.5);
|
||||
float saturation = 1.0f;
|
||||
float brightness = 0.8f + 0.2f * Math.max(0.0f, Math.min(1.0f, (float) y / 255));
|
||||
|
||||
return Color.getHSBColor(hue, saturation, brightness);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.eternalcode.combat.border.animation.block;
|
||||
|
||||
import eu.okaeri.configs.OkaeriConfig;
|
||||
import eu.okaeri.configs.annotation.Comment;
|
||||
import java.time.Duration;
|
||||
|
||||
public class BlockSettings extends OkaeriConfig {
|
||||
|
||||
@Comment("# Enable block animation?")
|
||||
public boolean enabled = true;
|
||||
|
||||
@Comment({
|
||||
"# Block type used for rendering the border",
|
||||
"# Custom: RAINBOW_GLASS, RAINBOW_WOOL, RAINBOW_TERRACOTTA, RAINBOW_CONCRETE",
|
||||
"# Vanilla: https://javadocs.packetevents.com/com/github/retrooper/packetevents/protocol/world/states/type/StateTypes.html"
|
||||
})
|
||||
public BlockType type = BlockType.RAINBOW_GLASS;
|
||||
|
||||
@Comment({
|
||||
"# Delay between each async animation update (need restart to apply)",
|
||||
"# Lower values will decrease performance but will make the animation smoother",
|
||||
"# Higher values will increase performance"
|
||||
})
|
||||
public Duration updateDelay = Duration.ofMillis(250);
|
||||
|
||||
@Comment({
|
||||
"# Delay between each chunk cache update (need restart to apply)",
|
||||
"# Lower values will decrease performance",
|
||||
"# Higher values will increase performance but may cause overlapping existing blocks (this does not modify the world)"
|
||||
})
|
||||
public Duration chunkCacheDelay = Duration.ofMillis(300);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.eternalcode.combat.border.animation.block;
|
||||
|
||||
import com.eternalcode.combat.border.BorderPoint;
|
||||
import static com.eternalcode.combat.border.animation.block.BorderBlockRainbowUtil.xyzToConcrete;
|
||||
import static com.eternalcode.combat.border.animation.block.BorderBlockRainbowUtil.xyzToGlass;
|
||||
import static com.eternalcode.combat.border.animation.block.BorderBlockRainbowUtil.xyzToTerracotta;
|
||||
import static com.eternalcode.combat.border.animation.block.BorderBlockRainbowUtil.xyzToWool;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import java.util.Locale;
|
||||
|
||||
public class BlockType {
|
||||
|
||||
public static final BlockType RAINBOW_GLASS = new BlockType("RAINBOW_GLASS", point -> xyzToGlass(point));
|
||||
public static final BlockType RAINBOW_TERRACOTTA = new BlockType("RAINBOW_TERRACOTTA", point -> xyzToTerracotta(point));
|
||||
public static final BlockType RAINBOW_WOOL = new BlockType("RAINBOW_WOOL", point -> xyzToWool(point));
|
||||
public static final BlockType RAINBOW_CONCRETE = new BlockType("RAINBOW_CONCRETE", point -> xyzToConcrete(point));
|
||||
|
||||
private final String name;
|
||||
private final TypeProvider type;
|
||||
|
||||
private BlockType(String name, TypeProvider type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public StateType getStateType(BorderPoint point) {
|
||||
return type.provide(point);
|
||||
}
|
||||
|
||||
public static BlockType fromName(String name) {
|
||||
return switch (name) {
|
||||
case "RAINBOW_GLASS" -> RAINBOW_GLASS;
|
||||
case "RAINBOW_WOOL" -> RAINBOW_WOOL;
|
||||
case "RAINBOW_TERRACOTTA" -> RAINBOW_TERRACOTTA;
|
||||
case "RAINBOW_CONCRETE" -> RAINBOW_CONCRETE;
|
||||
default -> new BlockType(name, point -> StateTypes.getByName(name.toLowerCase(Locale.ROOT)));
|
||||
};
|
||||
}
|
||||
|
||||
private interface TypeProvider {
|
||||
StateType provide(BorderPoint point);
|
||||
}
|
||||
|
||||
}
|
||||