Merge branch 'pr-5'

# Conflicts:
#	.github/CONTRIBUTING.md
#	.github/PULL_REQUEST_TEMPLATE.md
#	.github/workflows/gradle.yml
#	.github/workflows/hangar-publish.yml
#	.github/workflows/modrinth-publish.yml
#	buildSrc/src/main/kotlin/Versions.kt
#	eternalcombat-plugin/build.gradle.kts
#	eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java
This commit was merged in pull request #5.
This commit is contained in:
NotAlexNoyle committed 2026-04-22 13:38:44 -04:00
commit 8e93f90459
36 files changed
+892 -71

No files matched your search

+64
View File
@@ -0,0 +1,64 @@
# 👋 Contributing to EternalCombat
First off, thanks for taking the time to contribute! 🎉
We love user contributions and want to make it as easy as possible for you to get involved.
## 🛠️ Development Setup
To start contributing, you'll need to set up your environment:
1. **Java 21**: We use Java 21 for development. Ensure you have a JDK 21 installed.
2. **IDE**: We recommend [IntelliJ IDEA](https://www.jetbrains.com/idea/).
3. **Git**: Ensure you have Git installed.
### 📥 Getting the Code
```bash
# Fork the repository on GitHub first!
# https://github.com/EternalCodeTeam/EternalCombat/fork
git clone https://github.com/YOUR-USERNAME/EternalCombat.git
cd EternalCombat
```
### 🔨 Building the Project
Run this command in your terminal:
```bash
./gradlew build
```
## 🤝 How to Contribute
1. **Fork & Clone**: Fork the repo to your own account and clone it.
2. **Branch**: Create a new branch for your feature or fix.
```bash
git checkout -b feat/my-awesome-feature
```
3. **Code**: Hack away! 💻
4. **Test**: Run manual test by `runServer` to ensure you haven't broken anything.
```bash
./gradlew runServer
```
5. **Push**: Push your changes to your fork.
6. **Pull Request**: Open a PR against the `master` branch.
## 📏 Code Style
Make sure that your code adheres to the plugin's existing coding style.
## 🐛 Reporting Bugs
If you find a bug, please use the **Bug Report** issue template.
- Check if the issue already exists.
- Provide a minimal reproduction if possible.
- Include logs and screenshots.
## 💡 Feature Requests
Have an idea? Use the **Feature Request** issue template.
- Explain *why* this feature is useful.
- Describe how it should look or behave.
## 📄 License
By contributing, you agree that your contributions will be licensed under the project's [License](../LICENSE).
+84
View File
@@ -0,0 +1,84 @@
name: 🐛 Bug Report
description: File a bug report to help us improve EternalCombat
title: "[Bug]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
Please ensure that you have searched the existing issues to see if this bug has already been reported.
- type: textarea
id: description
attributes:
label: Bug description
description: A clear and concise description of what the bug is.
placeholder: I was doing X and Y happened...
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps to reproduce
description: Please provide a step-by-step guide to reproduce the issue.
placeholder: |
1. Launch the server
2. Tag yourself using the '/combat tag' command
3. See error
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
validations:
required: true
- type: textarea
id: screenshots
attributes:
label: Screenshots/Video
description: If applicable, add screenshots or a video to help explain your problem.
- type: dropdown
id: server-software
attributes:
label: Server Software
description: What server software are you using?
options:
- Paper
- Purpur
- Spigot
- Folia
- Other (please specify in logs)
validations:
required: true
- type: input
id: minecraft-version
attributes:
label: Minecraft Version
description: What version of Minecraft are you running? (e.g. 1.21.11)
placeholder: 1.21.11
validations:
required: true
- type: input
id: plugin-version
attributes:
label: Plugin Version
description: What version of EternalCombat are you running? (Check with /eternalcombat version)
placeholder: v2.4.1
validations:
required: true
- type: textarea
id: logs
attributes:
label: Logs / Stack Trace
description: Please paste any relevant logs or stack traces here. Use a pastebin service if the log is too long.
render: shell
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: 💬 Discord Community
url: https://discord.com/invite/FQ7jmGBd6c
about: Please ask questions and discuss ideas in our Discord server before opening a generic issue.
- name: 📖 Documentation
url: https://eternalcode.pl/docs/eternalcombat/introduction
about: Read the documentation to see if your question is already answered.
@@ -0,0 +1,38 @@
name: 💡 Feature Request
description: Suggest an idea for EternalCombat
title: "[Feature]: "
labels: ["feature"]
body:
- type: markdown
attributes:
value: |
Thanks for suggesting a new feature! Please explain your idea in detail.
- type: textarea
id: problem
attributes:
label: Is your feature request related to a problem?
description: Please describe the problem you are trying to solve.
placeholder: I'm always frustrated when...
validations:
required: true
- type: textarea
id: solution
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
- type: textarea
id: additional-context
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.
+35
View File
@@ -0,0 +1,35 @@
---
name: Pull Request
about: Submit a contribution to EternalCombat
title: 'GH-{NUMBER} <title>'
labels: []
assignees: []
---
## 📝 Description
<!--
Please submit your PR using the following title format:
feat: Description of the new feature
fix: Description of the bug fix
chore: Maintenance tasks
docs: Documentation changes
-->
<!-- Describe your changes clearly and concisely. Why is this change needed? What does it accomplish? -->
## 🎯 Type of Change
<!-- Please check the boxes that apply to your changes -->
- [ ] 🐛 Bug fix (non-breaking change which fixes an issue)
- [ ] ✨ New feature (non-breaking change which adds functionality)
- [ ] 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] 🔧 Refactoring (no functional changes, just code improvements)
- [ ] 🎨 Style/Cosmetic (formatting, whitespace, visual changes)
- [ ] 📚 Documentation (updates to documentation or README)
- [ ] 🤖 CI/CD/Build (changes to workflows, gradle, etc.)
**Test Environment:**
- **Minecraft Version:** <!-- e.g. 1.20.4 -->
- **Java Version:** <!-- e.g. 21 -->
- **Platform:** <!-- e.g. Windows/Linux/MacOS -->
## 📸 Screenshots (if applicable)
File renamed without changes.
+48
View File
@@ -0,0 +1,48 @@
name: Java CI with Gradle
on:
push:
branches: [ "master" ]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up JDK 17
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
- name: Set Variables & Build
uses: actions/github-script@v9
with:
script: |
const isPR = context.eventName === 'pull_request';
const branch = isPR ? context.payload.pull_request.head.ref.replace(/\//g, '-') : 'master';
const sha = (isPR ? context.payload.pull_request.head.sha : context.sha).substring(0, 7);
const artifactName = `EternalCombat ${branch}+${sha}`;
core.exportVariable('FULL_ARTIFACT_NAME', artifactName);
- name: Build with Gradle
run: ./gradlew shadowJar
- name: Upload build artifact
uses: actions/upload-artifact@v7
with:
name: ${{ env.FULL_ARTIFACT_NAME }}
path: eternalcombat-plugin/build/libs/*.jar
archive: false
+34
View File
@@ -0,0 +1,34 @@
name: Publish to Hangar
on:
push:
branches:
- master
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
if: (github.event_name == 'push' && !contains(github.event.head_commit.message, '[ci skip]')) || github.event_name == 'release'
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up JDK 21
uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
- name: Publish to Hangar
run: ./gradlew :eternalcombat-plugin:publishPluginPublicationToHangar
env:
HANGAR_API_TOKEN: ${{ secrets.HANGAR_API_TOKEN }}
GITHUB_EVENT_NAME: ${{ github.event_name }}
CHANGELOG: ${{ github.event_name == 'release' && github.event.release.body || github.event.head_commit.message }}
+34
View File
@@ -0,0 +1,34 @@
name: Publish to Modrinth
on:
push:
branches:
- master
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
if: (github.event_name == 'push' && !contains(github.event.head_commit.message, '[ci skip]')) || github.event_name == 'release'
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up JDK 21
uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
- name: Publish to Modrinth
run: ./gradlew :eternalcombat-plugin:modrinth
env:
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
GITHUB_EVENT_NAME: ${{ github.event_name }}
CHANGELOG: ${{ github.event_name == 'release' && github.event.release.body || github.event.head_commit.message }}
+13 -8
View File
@@ -10,7 +10,7 @@
[![Read the Docs](https://raw.githubusercontent.com/vLuckyyy/badges/main/read-the-documentation.svg)](https://docs.eternalcode.pl/eternalcombat/introduction)
[![Available on BStats](https://raw.githubusercontent.com/vLuckyyy/badges/main/available-on-bstats.svg)](https://bstats.org/plugin/bukkit/EternalCombat/17803)
[Report Bug](https://github.com/EternalCodeTeam/EternalCombat/issues) • [Request Feature](https://github.com/EternalCodeTeam/EternalCombat/issues) • [Join Discord](https://discord.gg/eternalcode)
[Report Bug](https://github.com/EternalCodeTeam/EternalCombat/issues) • [Request Feature](https://github.com/EternalCodeTeam/EternalCombat/issues) • [Join Discord](https://discord.com/invite/FQ7jmGBd6c)
<br>
@@ -29,7 +29,7 @@ EternalCombat 2.0 has been tested on Minecraft versions **1.17.1 to 1.21.11**, b
other versions too.
If you run into any compatibility issues, please let us know in
the [Issues tab](https://github.com/EternalCodeTeam/EternalCombat/issues).
The plugin requires **Java 17 or later**, so
The plugin requires **Java 21 or later**, so
ensure your server is ready.
Ready for action?
Install EternalCombat and dive in now!
@@ -43,17 +43,22 @@ fair, heart-pounding battles that keep players on their toes. Here’s a rundown
- **Combat Logging**
No more dodging fights by logging out! Once players are in combat, they’re committed until the showdown ends. Watch it
in action:
![Combat log anti logout feature](https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/combatlog.gif?raw=true)
<img alt="Combat log anti logout feature" src="https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/combatlog.gif?raw=true" width="420">
- **Customize combat experience**
Add custom effects to players in combat or death. Everything should be configurable and user-friendly:
<img alt="Lightning strikes when players die" src="https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/lightning.gif?raw=true" width="420">
<img alt="Flare indicates players death location" src="https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/flare.gif?raw=true" width="420">
- **Spawn Protection (Configurable)**
Stop players from fleeing to safety! Block access to spawn or safe zones during combat – tweak it to fit your server’s
rules. See how it works:
![Border around protected region](https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/border.gif?raw=true)
<img alt="Border around protected region" src="https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/border.gif?raw=true" width="420">
- **Crystal PvP support**
Engage in intense Crystal PvP battles without worrying about players logging out mid-fight! EternalCombat keeps
everyone in the game until the last anchor hit. Check it out:
![Crystal PvP showcase](https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/crystals.gif?raw=true)
everyone in the game until the last anchor hit. Check it out:
<img alt="Crystal PvP showcase" src="https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/crystals.gif?raw=true" width="420">
- **Fully Customizable Combat**
Tailor the combat experience to your liking with a ton of options! From disabling elytra to setting drop rates for
@@ -132,7 +137,7 @@ With Maven:
With Gradle:
```kts
compileOnly("com.eternalcode:eternalcombat-api:2.4.0")
compileOnly("com.eternalcode:eternalcombat-api:2.5.0-SNAPSHOT")
```
With Maven:
@@ -142,7 +147,7 @@ With Maven:
<dependency>
<groupId>com.eternalcode</groupId>
<artifactId>eternalcombat-api</artifactId>
<version>2.4.0</version>
<version>2.5.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
```
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 MiB

After

Width:  |  Height:  |  Size: 5.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 MiB

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 787 KiB

After

Width:  |  Height:  |  Size: 752 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 98 KiB

+2 -2
View File
@@ -11,8 +11,8 @@ repositories {
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.3.0")
implementation("com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:9.3.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:3.0.2")
}
+7 -6
View File
@@ -2,13 +2,13 @@ object Versions {
const val SPIGOT_API = "1.17.1-R0.1-SNAPSHOT"
const val JUNIT_JUPITER_API = "6.0.2"
const val JUNIT_JUPITER_PARAMS = "6.0.2"
const val JUNIT_JUPITER_ENGINE = "6.0.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 JETBRAINS_ANNOTATIONS = "26.0.2-1"
const val JETBRAINS_ANNOTATIONS = "26.1.0"
const val ETERNALCODE_COMMONS = "1.3.2"
const val ETERNALCODE_COMMONS = "1.3.4"
const val MULTIFICATION = "1.2.4"
const val PACKETEVENTS = "2.11.2"
@@ -19,6 +19,8 @@ object Versions {
const val OKAERI_CONFIGS_SERDES_COMMONS = "5.0.13"
const val OKAERI_CONFIGS_SERDES_BUKKIT = "5.0.13"
const val XSERIES = "13.6.0"
const val CAFFEINE = "3.2.3"
const val WORLD_GUARD_BUKKIT = "7.0.8"
@@ -33,4 +35,3 @@ object Versions {
}
@@ -3,7 +3,7 @@ plugins {
}
group = "com.eternalcode"
version = "2.4.1"
version = "2.5.0-SNAPSHOT"
tasks.compileJava {
options.compilerArgs = listOf("-Xlint:deprecation", "-parameters")
@@ -4,7 +4,7 @@ plugins {
}
group = "com.eternalcode"
version = "2.4.0"
version = "2.5.0-SNAPSHOT"
java {
withSourcesJar()
+6 -4
View File
@@ -1,6 +1,5 @@
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription
import io.papermc.hangarpublishplugin.model.Platforms
plugins {
`eternalcombat-java`
`eternalcombat-repositories`
@@ -8,7 +7,7 @@ plugins {
id("net.minecrell.plugin-yml.bukkit")
id("com.gradleup.shadow")
id("xyz.jpenilla.run-paper")
id("com.modrinth.minotaur") version "2.8.10"
id("com.modrinth.minotaur") version "2.9.0"
id("io.papermc.hangar-publish-plugin") version "0.1.4"
}
@@ -126,6 +125,8 @@ dependencies {
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}")
@@ -139,7 +140,7 @@ dependencies {
// PlaceholderAPI
compileOnly("me.clip:placeholderapi:${Versions.PLACEHOLDER_API}")
// Multification
implementation("com.eternalcode:multification-bukkit:${Versions.MULTIFICATION}")
implementation("com.eternalcode:multification-okaeri:${Versions.MULTIFICATION}")
@@ -208,7 +209,8 @@ tasks.shadowJar {
"com.github.benmanes.caffeine",
"com.eternalcode.commons",
"com.eternalcode.multification",
"io.papermc.lib"
"com.github.cryptomorin",
"io.papermc.lib",
).forEach { pack ->
relocate(pack, "$prefix.$pack")
}
@@ -19,6 +19,9 @@ 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;
@@ -96,15 +99,11 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
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();
EventManager eventManager = new EventManager(this);
PluginConfig pluginConfig = configService.create(PluginConfig.class, configFile);
PluginConfig pluginConfig = configService.create(PluginConfig.class, new File(dataFolder, CONFIG_RESOURCE_PATH));
MinecraftScheduler scheduler = CombatSchedulerAdapter.getAdaptiveScheduler(this);
@@ -177,6 +176,8 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
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),
@@ -187,7 +188,8 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
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 FireworkController(this.fightManager, pluginConfig, noticeService),
new FightInventoryController(this.fightManager, pluginConfig, noticeService)
);
eventManager.subscribe(
@@ -222,8 +224,6 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
this.fightManager.untagAll();
}
@Override
public FightManager getFightManager() {
return this.fightManager;
@@ -39,12 +39,6 @@ public class CombatSettings extends OkaeriConfig {
})
public boolean disableFireworks = true;
@Comment({
"# Prevent players from opening their inventory during combat.",
"# Set to 'true' to block inventory access while in combat."
})
public boolean disableInventoryAccess = false;
@Comment({
"# Enable or disable combat logging for damage caused by non-player entities.",
"# Set to 'true' to log damage from non-player sources, or 'false' to disable this feature."
@@ -0,0 +1,38 @@
package com.eternalcode.combat.config.implementation;
public enum InventoryAccessMode {
/**
* Allow access to all inventories during combat
*/
ALLOW_ALL,
/**
* Block access to all inventories during combat
*/
BLOCK_ALL,
/**
* Only block inventories in the restricted list
*/
BLACKLIST,
/**
* Only allow inventories in the allowed list
*/
WHITELIST;
/**
* Determines if an inventory type should be blocked based on the mode and whether it's in the list
*
* @param isInList whether the inventory type is in the configured list
* @return true if the inventory should be blocked
*/
public boolean shouldBlock(boolean isInList) {
return switch (this) {
case ALLOW_ALL -> false;
case BLOCK_ALL -> true;
case BLACKLIST -> isInList;
case WHITELIST -> !isInList;
};
}
}
@@ -0,0 +1,38 @@
package com.eternalcode.combat.config.implementation;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment;
import org.bukkit.event.inventory.InventoryType;
import java.util.List;
public class InventorySettings extends OkaeriConfig {
@Comment({
"# Inventory access restriction mode during combat",
"# ALLOW_ALL - Players can open all inventories during combat",
"# BLOCK_ALL - Players cannot open any inventories during combat",
"# BLACKLIST - Block only inventories listed in restrictedInventoryTypes",
"# WHITELIST - Allow only inventories listed in restrictedInventoryTypes"
})
public InventoryAccessMode inventoryAccessMode = InventoryAccessMode.ALLOW_ALL;
@Comment({
"# List of inventory types to restrict or allow (depending on the mode)",
"# In BLACKLIST mode: these inventories will be blocked",
"# In WHITELIST mode: only these inventories will be allowed",
"# Available types: CHEST, ENDER_CHEST, DISPENSER, DROPPER, FURNACE, WORKBENCH,",
"# CRAFTING, ENCHANTING, BREWING, MERCHANT, BEACON, ANVIL, SMITHING, HOPPER,",
"# SHULKER_BOX, BARREL, BLAST_FURNACE, LECTERN, SMOKER, LOOM, CARTOGRAPHY,",
"# GRINDSTONE, STONECUTTER, COMPOSTER, CHISELED_BOOKSHELF, JUKEBOX, SMITHING_NEW,",
"# CRAFTER, DECORATED_POT",
"# Note: PLAYER and CREATIVE inventories are never blocked to prevent game-breaking issues",
"# Full list: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/InventoryType.html"
})
public List<InventoryType> restrictedInventoryTypes = List.of(
InventoryType.CHEST,
InventoryType.ENDER_CHEST,
InventoryType.BARREL,
InventoryType.SHULKER_BOX
);
}
@@ -1,6 +1,7 @@
package com.eternalcode.combat.config.implementation;
import com.eternalcode.combat.border.BorderSettings;
import com.eternalcode.combat.fight.death.DeathSettings;
import com.eternalcode.combat.fight.drop.DropSettings;
import com.eternalcode.combat.fight.effect.FightEffectSettings;
import com.eternalcode.combat.fight.knockback.KnockbackSettings;
@@ -42,6 +43,12 @@ public class PluginConfig extends OkaeriConfig {
})
public FightEffectSettings effect = new FightEffectSettings();
@Comment({
" ",
"# This section contains effects displayed on death of the player"
})
public DeathSettings death = new DeathSettings();
@Comment({
" ",
"# Customize how items are dropped when a player dies during combat.",
@@ -105,6 +112,13 @@ public class PluginConfig extends OkaeriConfig {
})
public CombatSettings combat = new CombatSettings();
@Comment({
" ",
"# Settings related to inventory access during combat.",
"# Configure which inventories players can or cannot access while in combat."
})
public InventorySettings inventory = new InventorySettings();
@Comment({
" ",
"# Settings related to placeholders used in the plugin.",
@@ -17,6 +17,7 @@ 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.inventory.InventoryType;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerToggleFlightEvent;
@@ -185,28 +186,6 @@ public class FightActionBlockerController implements Listener {
}
}
@EventHandler
void onOpenInventory(InventoryOpenEvent event) {
if (!this.config.combat.disableInventoryAccess) {
return;
}
Player player = (Player) event.getPlayer();
UUID uniqueId = player.getUniqueId();
if (!this.fightManager.isInCombat(uniqueId)) {
return;
}
event.setCancelled(true);
this.noticeService.create()
.player(uniqueId)
.notice(this.config.messagesSettings.inventoryBlockedDuringCombat)
.send();
}
@EventHandler
void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
Player player = event.getPlayer();
@@ -0,0 +1,49 @@
package com.eternalcode.combat.fight.controller;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.notification.NoticeService;
import java.util.UUID;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.inventory.InventoryType;
public class FightInventoryController implements Listener {
private final FightManager fightManager;
private final PluginConfig config;
private final NoticeService noticeService;
public FightInventoryController(FightManager fightManager, PluginConfig config, NoticeService noticeService) {
this.fightManager = fightManager;
this.config = config;
this.noticeService = noticeService;
}
@EventHandler
void onOpenInventory(InventoryOpenEvent event) {
Player player = (Player) event.getPlayer();
UUID uniqueId = player.getUniqueId();
if (!this.fightManager.isInCombat(uniqueId)) {
return;
}
InventoryType inventoryType = event.getInventory().getType();
boolean isInRestrictedList = this.config.inventory.restrictedInventoryTypes.contains(inventoryType);
boolean shouldBlock = this.config.inventory.inventoryAccessMode.shouldBlock(isInRestrictedList);
if (shouldBlock) {
event.setCancelled(true);
this.noticeService.create()
.player(uniqueId)
.notice(this.config.messagesSettings.inventoryBlockedDuringCombat)
.send();
}
}
}
@@ -0,0 +1,152 @@
package com.eternalcode.combat.fight.death;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.event.CauseOfUnTag;
import com.eternalcode.combat.fight.event.FightUntagEvent;
import com.eternalcode.commons.scheduler.Scheduler;
import java.time.Duration;
import java.util.UUID;
import org.bukkit.Color;
import org.bukkit.FireworkEffect;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.entity.Firework;
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.EntityDamageByEntityEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.inventory.meta.FireworkMeta;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.Plugin;
public class DeathFlareController implements Listener {
private final PluginConfig pluginConfig;
private final Server server;
private final Scheduler scheduler;
private final NamespacedKey key;
public DeathFlareController(PluginConfig pluginConfig, Server server, Scheduler scheduler, Plugin plugin) {
this.pluginConfig = pluginConfig;
this.server = server;
this.scheduler = scheduler;
this.key = NamespacedKey.fromString("eternalcombat_firework", plugin);
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onFightUntagEvent(FightUntagEvent event) {
CauseOfUnTag cause = event.getCause();
if (cause != CauseOfUnTag.DEATH && cause != CauseOfUnTag.DEATH_BY_PLAYER) {
return;
}
UUID uniqueId = event.getPlayer();
Player player = this.server.getPlayer(uniqueId);
if (player == null) {
return;
}
if (this.pluginConfig.death.firework.inCombat && !this.pluginConfig.death.firework.afterEveryDeath) {
this.spawnFlare(player);
}
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPlayerDeathEventFlare(PlayerDeathEvent event) {
Player player = event.getEntity();
if (this.pluginConfig.death.firework.afterEveryDeath) {
this.spawnFlare(player);
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onEntityDamageByEntity(EntityDamageByEntityEvent event) {
if (event.getDamager() instanceof Firework firework && firework.getPersistentDataContainer().has(key, PersistentDataType.STRING)) {
event.setCancelled(true);
}
}
private void spawnFlare(Player player) {
Location deathLocation = player.getLocation();
World world = deathLocation.getWorld();
Firework flare = world.spawn(deathLocation, Firework.class);
flare.getPersistentDataContainer().set(key, PersistentDataType.STRING, "true");
FireworkMeta meta = flare.getFireworkMeta();
Color primaryColor = this.decodeColor(this.pluginConfig.death.firework.primaryColor, "primary");
Color fadeColor = this.decodeColor(this.pluginConfig.death.firework.fadeColor, "fade");
FireworkEffect effect = FireworkEffect.builder()
.with(this.pluginConfig.death.firework.fireworkType)
.withColor(primaryColor)
.withFade(fadeColor)
.trail(true)
.flicker(true)
.build();
meta.addEffect(effect);
meta.setPower(this.pluginConfig.death.firework.power);
flare.setFireworkMeta(meta);
if (this.pluginConfig.death.firework.particlesEnabled) {
scheduleParticles(flare, world);
}
}
private void scheduleParticles(Firework flare, World world) {
this.scheduler.runLaterAsync(
() -> {
if (flare.isDead() || !flare.isValid()) {
return;
}
this.spawnParticles(world, flare);
this.scheduleParticles(flare, world);
}, Duration.ofMillis(50)
);
}
private Color decodeColor(String firework, String name) {
try {
return Color.fromRGB(Integer.decode(firework));
}
catch (NumberFormatException exception) {
throw new IllegalArgumentException(
"Invalid " + name + " format in plugin configuration" + firework,
exception
);
}
}
private void spawnParticles(World world, Firework flare) {
Location location = flare.getLocation();
world.spawnParticle(
this.pluginConfig.death.firework.mainParticle.get(),
location,
this.pluginConfig.death.firework.mainParticleCount,
0.05,
0.05,
0.05,
0.01
);
world.spawnParticle(
this.pluginConfig.death.firework.secondaryParticle.get(),
location,
this.pluginConfig.death.firework.secondaryParticleCount,
0.1,
0.1,
0.1,
0.01
);
}
}
@@ -0,0 +1,56 @@
package com.eternalcode.combat.fight.death;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.event.CauseOfUnTag;
import com.eternalcode.combat.fight.event.FightUntagEvent;
import java.util.UUID;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
public class DeathLightningController implements Listener {
private final PluginConfig pluginConfig;
private final Server server;
public DeathLightningController(PluginConfig pluginConfig, Server server) {
this.pluginConfig = pluginConfig;
this.server = server;
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onFightUntagEvent(FightUntagEvent event) {
CauseOfUnTag cause = event.getCause();
if (cause != CauseOfUnTag.DEATH && cause != CauseOfUnTag.DEATH_BY_PLAYER) {
return;
}
UUID uniqueId = event.getPlayer();
Player player = this.server.getPlayer(uniqueId);
if (player == null) {
return;
}
if (this.pluginConfig.death.lightning.inCombat && !this.pluginConfig.death.lightning.afterEveryDeath) {
this.lightningStrike(player);
}
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPlayerDeathEventLightning(PlayerDeathEvent event) {
Player player = event.getEntity();
if (this.pluginConfig.death.lightning.afterEveryDeath) {
lightningStrike(player);
}
}
private void lightningStrike(Player player) {
player.getWorld().strikeLightningEffect(player.getLocation());
}
}
@@ -0,0 +1,77 @@
package com.eternalcode.combat.fight.death;
import com.cryptomorin.xseries.particles.XParticle;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment;
import org.bukkit.FireworkEffect;
public class DeathSettings extends OkaeriConfig {
@Comment({
"Settings related to lightning effect upon death",
"Setting both afterEveryDeath and inCombat to false will disable this feature completely"
})
public LightningSettings lightning = new LightningSettings();
public static class LightningSettings extends OkaeriConfig {
@Comment("Should lightning spawn on every death?")
public boolean afterEveryDeath = false;
@Comment("Should lightning spawn on ONLY deaths in combat?")
public boolean inCombat = true;
}
@Comment({
"Settings for the Arc Raiders style flare (firework)",
"Setting both afterEveryDeath and inCombat to false will disable this feature completely"
})
public FlareSettings firework = new FlareSettings();
public static class FlareSettings extends OkaeriConfig {
@Comment("Should firework (flare) spawn on every death?")
public boolean afterEveryDeath = false;
@Comment("Should firework (flare) spawn on ONLY deaths in combat?")
public boolean inCombat = false;
@Comment("Power of firework - how long till explosion (please enter positive number)")
public int power = 2;
@Comment({
"The firework (flare) effect type (BALL, BALL_LARGE, STAR, BURST, CREEPER)",
"Reference: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/FireworkEffect.Type.html"
})
public FireworkEffect.Type fireworkType = FireworkEffect.Type.BALL;
@Comment("Hex color for the firework (flare)")
public String primaryColor = "#a80022";
@Comment("Hex color for the fade of firework (flare)")
public String fadeColor = "#0a0a0a";
@Comment("Toggle on/off additional particles spawned in the firework (flare) path")
public boolean particlesEnabled = true;
@Comment({
"The main trail particle (e.g., CAMPFIRE_COSY_SMOKE, SMOKE_LARGE)",
"Reference: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Particle.html"
})
public XParticle mainParticle = XParticle.CAMPFIRE_COSY_SMOKE;
@Comment("Count of main particles spawned on each tick")
public int mainParticleCount = 3;
@Comment({
"The secondary trail particle (e.g., CAMPFIRE_COSY_SMOKE, SMOKE_LARGE)",
"Reference: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Particle.html"
})
public XParticle secondaryParticle = XParticle.SMALL_FLAME;
@Comment("Count of secondary particles spawned on each tick")
public int secondaryParticleCount = 3;
}
}
@@ -9,12 +9,15 @@ import java.time.Duration;
import java.util.Optional;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.entity.Entity;
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;
import org.bukkit.event.vehicle.VehicleMoveEvent;
import org.spigotmc.event.entity.EntityMountEvent;
public class KnockbackRegionController implements Listener {
@@ -89,6 +92,67 @@ public class KnockbackRegionController implements Listener {
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
void onVehicleMove(VehicleMoveEvent event) {
Location locationTo = event.getTo();
Location locationFrom = event.getFrom();
if (locationTo.getBlockX() == locationFrom.getBlockX()
&& locationTo.getBlockY() == locationFrom.getBlockY()
&& locationTo.getBlockZ() == locationFrom.getBlockZ()) {
return;
}
for (Entity passenger : event.getVehicle().getPassengers()) {
if (!(passenger instanceof Player player)) {
continue;
}
if (!this.fightManager.isInCombat(player.getUniqueId())) {
continue;
}
Optional<Region> regionOptional = this.regionProvider.getRegion(locationTo);
if (regionOptional.isEmpty()) {
return;
}
Region region = regionOptional.get();
if (region.contains(locationFrom)) {
this.knockbackService.knockback(region, player);
this.knockbackService.forceKnockbackLater(player, region);
} else {
this.knockbackService.knockbackLater(region, player, Duration.ofMillis(50));
}
this.noticeService.create()
.player(player.getUniqueId())
.notice(config -> config.messagesSettings.cantEnterOnRegion)
.send();
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
void onEntityMount(EntityMountEvent event) {
if (!(event.getEntity() instanceof Player player)) {
return;
}
if (!this.fightManager.isInCombat(player.getUniqueId())) {
return;
}
if (!this.regionProvider.isInRegion(event.getMount().getLocation())) {
return;
}
event.setCancelled(true);
this.noticeService.create()
.player(player.getUniqueId())
.notice(config -> config.messagesSettings.cantEnterOnRegion)
.send();
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
void onTag(FightTagEvent event) {
Player player = this.server.getPlayer(event.getPlayer());
@@ -41,19 +41,22 @@ public final class KnockbackService {
insideRegion.put(player.getUniqueId(), region);
scheduler.runLater(
player.getLocation(), () -> {
insideRegion.remove(player.getUniqueId());
Location playerLocation = player.getLocation();
if (!region.contains(playerLocation) && !regionProvider.isInRegion(playerLocation)) {
return;
}
scheduler.runLater(player.getLocation(), () -> {
insideRegion.remove(player.getUniqueId());
Location location =
generate(playerLocation, Point2D.from(region.getMin()), Point2D.from(region.getMax()));
Location playerLocation = player.getLocation();
if (!region.contains(playerLocation) && !regionProvider.isInRegion(playerLocation)) {
return;
}
PaperLib.teleportAsync(player, location, TeleportCause.PLUGIN);
}, this.config.knockback.forceDelay);
if (player.isInsideVehicle()) {
player.leaveVehicle();
}
Location location = generate(playerLocation, Point2D.from(region.getMin()), Point2D.from(region.getMax()));
PaperLib.teleportAsync(player, location, TeleportCause.PLUGIN);
}, this.config.knockback.forceDelay);
}
private Location generate(Location playerLocation, Point2D minX, Point2D maxX) {
@@ -68,6 +71,10 @@ public final class KnockbackService {
}
public void knockback(Region region, Player player) {
if (player.isInsideVehicle()) {
player.leaveVehicle();
}
Point point = region.getCenter();
Location subtract = player.getLocation().subtract(point.x(), 0, point.z());
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+1 -1
View File
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.